430 lines
13 KiB
PHP
Executable File
430 lines
13 KiB
PHP
Executable File
<?php
|
||
/**
|
||
* HTTP / Cookie 辅助函数(ip_check 与 f_check 共用)
|
||
*/
|
||
|
||
if (!function_exists('cloak_get_SERVER_value')) {
|
||
function cloak_get_SERVER_value($field_name)
|
||
{
|
||
return isset($_SERVER[$field_name]) ? $_SERVER[$field_name] : null;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('get_SERVER_value')) {
|
||
function get_SERVER_value($field_name)
|
||
{
|
||
return cloak_get_SERVER_value($field_name);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_request_is_https')) {
|
||
/**
|
||
* 访客是否通过 HTTPS 访问(含 Cloudflare / 反代 X-Forwarded-Proto)
|
||
*/
|
||
function cloak_request_is_https()
|
||
{
|
||
if (!empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off' && $_SERVER['HTTPS'] !== '0') {
|
||
return true;
|
||
}
|
||
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower((string) $_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') {
|
||
return true;
|
||
}
|
||
if (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && strtolower((string) $_SERVER['HTTP_X_FORWARDED_SSL']) === 'on') {
|
||
return true;
|
||
}
|
||
if (!empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower((string) $_SERVER['HTTP_FRONT_END_HTTPS']) === 'on') {
|
||
return true;
|
||
}
|
||
if (!empty($_SERVER['HTTP_CF_VISITOR'])) {
|
||
$cf = json_decode((string) $_SERVER['HTTP_CF_VISITOR'], true);
|
||
if (is_array($cf) && ($cf['scheme'] ?? '') === 'https') {
|
||
return true;
|
||
}
|
||
}
|
||
if (!empty($_SERVER['SERVER_PORT']) && (int) $_SERVER['SERVER_PORT'] === 443) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_current_request_url')) {
|
||
/**
|
||
* 浏览器实际访问的完整 URL(用于日志 page 字段;无 query 时不拼裸 ?)
|
||
*/
|
||
function cloak_current_request_url()
|
||
{
|
||
$scheme = cloak_request_is_https() ? 'https' : 'http';
|
||
$host = (string) ($_SERVER['HTTP_HOST'] ?? 'localhost');
|
||
|
||
if (!empty($_SERVER['REQUEST_URI'])) {
|
||
$uri = (string) $_SERVER['REQUEST_URI'];
|
||
if (strpos($uri, '://') !== false) {
|
||
return $uri;
|
||
}
|
||
if ($uri === '' || $uri[0] !== '/') {
|
||
$uri = '/' . ltrim($uri, '/');
|
||
}
|
||
return $scheme . '://' . $host . $uri;
|
||
}
|
||
|
||
$path = (string) ($_SERVER['SCRIPT_NAME'] ?? $_SERVER['PHP_SELF'] ?? '/');
|
||
$qs = (string) ($_SERVER['QUERY_STRING'] ?? '');
|
||
$url = $scheme . '://' . $host . $path;
|
||
if ($qs !== '') {
|
||
$url .= '?' . $qs;
|
||
}
|
||
return $url;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_encode_visitor_cookies')) {
|
||
function cloak_encode_visitor_cookies()
|
||
{
|
||
$transmit_string = '';
|
||
foreach ($_COOKIE as $name => $value) {
|
||
try {
|
||
$transmit_string .= "$name=$value; ";
|
||
} catch (Exception $e) {
|
||
continue;
|
||
}
|
||
}
|
||
return $transmit_string;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('encode_visitor_cookies')) {
|
||
function encode_visitor_cookies()
|
||
{
|
||
return cloak_encode_visitor_cookies();
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_forward_response_cookies')) {
|
||
function cloak_forward_response_cookies($ch, $headerLine)
|
||
{
|
||
if (preg_match('/^Set-Cookie:/mi', $headerLine, $cookie)) {
|
||
header($headerLine, false);
|
||
}
|
||
return strlen($headerLine);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('forward_response_cookies')) {
|
||
function forward_response_cookies($ch, $headerLine)
|
||
{
|
||
return cloak_forward_response_cookies($ch, $headerLine);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_write_nt_list')) {
|
||
function cloak_write_nt_list($cong_name, $nt, $ot)
|
||
{
|
||
$cong_name = parse_url($cong_name, PHP_URL_PATH);
|
||
$content = $cong_name . '||||||' . $nt . "\n";
|
||
$file = 'nt.txt';
|
||
|
||
if ($ot) {
|
||
$handle = @fopen($file, 'r+');
|
||
$search = '/' . $cong_name . '\|\|\|\|\|\|' . $ot . '/';
|
||
$found = false;
|
||
$tempContent = '';
|
||
|
||
while (!feof($handle)) {
|
||
$line = fgets($handle);
|
||
if (preg_match($search, $line)) {
|
||
$tempContent .= $content;
|
||
$found = true;
|
||
} else {
|
||
$tempContent .= $line;
|
||
}
|
||
}
|
||
|
||
if ($found && flock($handle, LOCK_EX)) {
|
||
file_put_contents($file, $tempContent);
|
||
flock($handle, LOCK_UN);
|
||
}
|
||
fclose($handle);
|
||
} else {
|
||
$handle = @fopen($file, 'a');
|
||
if ($handle && flock($handle, LOCK_EX)) {
|
||
fwrite($handle, $content);
|
||
flock($handle, LOCK_UN);
|
||
}
|
||
if ($handle) {
|
||
fclose($handle);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!function_exists('write_nt_list')) {
|
||
function write_nt_list($cong_name, $nt, $ot)
|
||
{
|
||
cloak_write_nt_list($cong_name, $nt, $ot);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_normalize_request_host')) {
|
||
/**
|
||
* 规范化请求 Host:小写、去端口、去首尾空白。
|
||
*
|
||
* @param string|null $host
|
||
*/
|
||
function cloak_normalize_request_host($host = null)
|
||
{
|
||
$host = strtolower(trim((string) ($host ?? ($_SERVER['HTTP_HOST'] ?? ''))));
|
||
if ($host === '') {
|
||
return '';
|
||
}
|
||
if ($host[0] === '[') {
|
||
$end = strpos($host, ']');
|
||
if ($end !== false) {
|
||
return substr($host, 0, $end + 1);
|
||
}
|
||
}
|
||
$colon = strpos($host, ':');
|
||
if ($colon !== false) {
|
||
$host = substr($host, 0, $colon);
|
||
}
|
||
return rtrim($host, '.');
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_host_is_ip')) {
|
||
/**
|
||
* @param string $host
|
||
*/
|
||
function cloak_host_is_ip($host)
|
||
{
|
||
$host = trim($host);
|
||
if ($host === '') {
|
||
return false;
|
||
}
|
||
if ($host[0] === '[' && substr($host, -1) === ']') {
|
||
return filter_var(substr($host, 1, -1), FILTER_VALIDATE_IP) !== false;
|
||
}
|
||
return filter_var($host, FILTER_VALIDATE_IP) !== false;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_host_is_local')) {
|
||
/**
|
||
* localhost / 单标签主机名(不含点)视为 host-only Cookie。
|
||
*
|
||
* @param string $host
|
||
*/
|
||
function cloak_host_is_local($host)
|
||
{
|
||
$host = strtolower(trim($host));
|
||
if ($host === '' || strpos($host, '.') === false) {
|
||
return true;
|
||
}
|
||
return $host === 'localhost'
|
||
|| substr($host, -6) === '.local'
|
||
|| substr($host, -5) === '.test'
|
||
|| substr($host, -13) === '.localhost';
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_cookie_public_suffixes')) {
|
||
/**
|
||
* 常见多级公共后缀(co.uk / com.cn 等),避免 Cookie domain 算成 .co.uk。
|
||
*
|
||
* @return string[]
|
||
*/
|
||
function cloak_cookie_public_suffixes()
|
||
{
|
||
return [
|
||
'co.uk', 'org.uk', 'ac.uk', 'gov.uk', 'net.uk',
|
||
'com.cn', 'net.cn', 'org.cn', 'gov.cn',
|
||
'com.au', 'net.au', 'org.au', 'edu.au',
|
||
'co.jp', 'ne.jp', 'or.jp',
|
||
'com.br', 'com.mx', 'com.tw', 'com.hk', 'co.nz', 'co.kr', 'com.sg',
|
||
'com.tr', 'com.pl', 'com.ar', 'com.co', 'co.in', 'com.my',
|
||
];
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_normalize_cookie_domain_config')) {
|
||
/**
|
||
* 配置项 CLOAK_COOKIE_DOMAIN → Cookie 用 domain(带点前缀,如 .shili.buzz)
|
||
*
|
||
* @param string $value
|
||
*/
|
||
function cloak_normalize_cookie_domain_config($value)
|
||
{
|
||
$value = strtolower(trim($value));
|
||
if ($value === '') {
|
||
return '';
|
||
}
|
||
$value = ltrim($value, '.');
|
||
if ($value === '' || cloak_host_is_ip($value) || cloak_host_is_local($value)) {
|
||
return '';
|
||
}
|
||
return '.' . $value;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_registrable_domain')) {
|
||
/**
|
||
* 从 Host 推断可注册域(不含前导点),IP/localhost 返回空。
|
||
*
|
||
* @param string|null $host
|
||
*/
|
||
function cloak_registrable_domain($host = null)
|
||
{
|
||
if (defined('CLOAK_COOKIE_DOMAIN') && (string) CLOAK_COOKIE_DOMAIN !== '') {
|
||
$configured = cloak_normalize_cookie_domain_config((string) CLOAK_COOKIE_DOMAIN);
|
||
return $configured !== '' ? ltrim($configured, '.') : '';
|
||
}
|
||
|
||
$host = cloak_normalize_request_host($host);
|
||
if ($host === '' || cloak_host_is_ip($host) || cloak_host_is_local($host)) {
|
||
return '';
|
||
}
|
||
|
||
$parts = explode('.', $host);
|
||
$count = count($parts);
|
||
if ($count < 2) {
|
||
return '';
|
||
}
|
||
|
||
$suffix2 = $parts[$count - 2] . '.' . $parts[$count - 1];
|
||
if (in_array($suffix2, cloak_cookie_public_suffixes(), true)) {
|
||
if ($count < 3) {
|
||
return '';
|
||
}
|
||
return $parts[$count - 3] . '.' . $suffix2;
|
||
}
|
||
|
||
return $suffix2;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_fingerprint_cookie_domain')) {
|
||
/**
|
||
* 指纹 / Session Cookie 跨子域 domain(带点,如 .shili.buzz);IP/localhost 返回空(host-only)。
|
||
*/
|
||
function cloak_fingerprint_cookie_domain()
|
||
{
|
||
if (defined('CLOAK_COOKIE_DOMAIN') && (string) CLOAK_COOKIE_DOMAIN !== '') {
|
||
return cloak_normalize_cookie_domain_config((string) CLOAK_COOKIE_DOMAIN);
|
||
}
|
||
|
||
$registrable = cloak_registrable_domain();
|
||
return $registrable !== '' ? '.' . $registrable : '';
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_fingerprint_js_cookie_options')) {
|
||
/**
|
||
* 前端 js-cookie 写入指纹 Cookie 的选项(与 PHP setcookie 策略一致)。
|
||
*
|
||
* @return array{path:string,expires:int,sameSite:string,domain?:string,secure?:bool}
|
||
*/
|
||
function cloak_fingerprint_js_cookie_options()
|
||
{
|
||
$opts = [
|
||
'path' => '/',
|
||
'expires' => 1,
|
||
'sameSite' => 'Lax',
|
||
];
|
||
$domain = cloak_fingerprint_cookie_domain();
|
||
if ($domain !== '') {
|
||
$opts['domain'] = $domain;
|
||
}
|
||
if (cloak_request_is_https()) {
|
||
$opts['secure'] = true;
|
||
}
|
||
return $opts;
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_set_cross_domain_cookie')) {
|
||
function cloak_set_cross_domain_cookie($name, $value, $expire)
|
||
{
|
||
$cookieDomain = cloak_fingerprint_cookie_domain();
|
||
$secure = cloak_request_is_https();
|
||
|
||
if (PHP_VERSION_ID >= 70300) {
|
||
$opts = [
|
||
'expires' => (int) $expire,
|
||
'path' => '/',
|
||
'secure' => $secure,
|
||
'httponly' => false,
|
||
'samesite' => 'Lax',
|
||
];
|
||
if ($cookieDomain !== '') {
|
||
$opts['domain'] = ltrim($cookieDomain, '.');
|
||
}
|
||
setcookie($name, $value, $opts);
|
||
return;
|
||
}
|
||
|
||
if ($cookieDomain === '') {
|
||
setcookie($name, $value, (int) $expire, '/', '', $secure, false);
|
||
return;
|
||
}
|
||
setcookie($name, $value, (int) $expire, '/', $cookieDomain, $secure, false);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('setCrossDomainCookie')) {
|
||
function setCrossDomainCookie($name, $value, $expire)
|
||
{
|
||
cloak_set_cross_domain_cookie($name, $value, $expire);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_fingerprint_time_param_present')) {
|
||
/**
|
||
* URL 是否已带 time 参数(表示前端指纹页已执行过一次跳转)
|
||
*/
|
||
function cloak_fingerprint_time_param_present()
|
||
{
|
||
return isset($_GET['time']) && (string) $_GET['time'] !== '';
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_fingerprint_cookies_ready')) {
|
||
/**
|
||
* 前端指纹 Cookie 是否已写入
|
||
*/
|
||
function cloak_fingerprint_cookies_ready()
|
||
{
|
||
return isset($_COOKIE['ctime'], $_COOKIE['cyyek'], $_COOKIE['cl']);
|
||
}
|
||
}
|
||
|
||
if (!function_exists('cloak_fingerprint_mmr_reason')) {
|
||
/**
|
||
* 根据 mmr Cookie 返回中文拒绝理由
|
||
*/
|
||
function cloak_fingerprint_mmr_reason()
|
||
{
|
||
if (!isset($_COOKIE['mmr'])) {
|
||
return '指纹检测未通过(未知原因)';
|
||
}
|
||
|
||
$mmrMap = [
|
||
1 => '语言包含中文',
|
||
2 => 'PC端客户不允许访问',
|
||
3 => '操作系统非IOS或者安卓',
|
||
4 => 'IPHONE 机型不符合要求',
|
||
11 => '非IPHONE机型',
|
||
12 => '自动化/WebDriver',
|
||
13 => '模拟器或虚拟机UA',
|
||
14 => '爬虫Bot UA',
|
||
15 => 'WebGL软件渲染',
|
||
16 => '环境不一致(平台/触摸/窗口)',
|
||
17 => '移动GPU与UA不符',
|
||
18 => '移动端无有效加速度计/陀螺仪',
|
||
];
|
||
|
||
$mmr = (int) $_COOKIE['mmr'];
|
||
|
||
return $mmrMap[$mmr] ?? '指纹检测未通过(未知原因)';
|
||
}
|
||
}
|