feat: xxbb

This commit is contained in:
hashbro
2026-08-14 20:16:36 +08:00
parent 01d1ef76a8
commit 5453d9d69b
5 changed files with 84 additions and 8 deletions
+26
View File
@@ -40,3 +40,29 @@ if (! function_exists('create_log')) {
}
}
}
if (! function_exists('c2_log_request_meta')) {
/**
* IP + proxy headers for C2 file logs (iptj vs native comparison).
*
* @return array{ip: string|null, remote_addr: mixed, headers: array<string, string|null>}
*/
function c2_log_request_meta(\Illuminate\Http\Request $request): array
{
$headers = [];
foreach ([
'x-ts', 'x-hash', 'sdkv', 'ver', 'accept', 'content-type', 'user-agent',
'host', 'cf-connecting-ip', 'cf-ipcountry', 'x-forwarded-for', 'x-real-ip',
] as $h) {
if ($request->headers->has($h)) {
$headers[$h] = $request->headers->get($h);
}
}
return [
'ip' => $request->ip(),
'remote_addr' => $request->server->get('REMOTE_ADDR'),
'headers' => $headers,
];
}
}
@@ -59,6 +59,7 @@ class XxbbC2Controller extends Controller
$domain = PageVisit::normalizeDomain($domain);
$ip = PageVisit::normalizeIp((string) $request->ip());
$uid = PageVisit::visitorUid($domain, $ip);
$recorded = false;
if ($channelCode !== '' && strlen($channelCode) <= 64 && $this->shouldRecordIptj($channelCode, $uid, $sessionId)) {
$ua = substr((string) $request->userAgent(), 0, 512);
@@ -83,6 +84,7 @@ class XxbbC2Controller extends Controller
'referer' => $this->referer($request),
'created_at' => now(),
]);
$recorded = true;
} catch (QueryException $e) {
if (($e->errorInfo[0] ?? '') !== '23000') {
throw $e;
@@ -90,6 +92,23 @@ class XxbbC2Controller extends Controller
}
}
$meta = c2_log_request_meta($request);
create_log([
'dir' => 'in',
'method' => $request->method(),
'path' => '/'.ltrim($request->path(), '/'),
'ip' => $ip !== '' ? $ip : $meta['ip'],
'remote_addr' => $meta['remote_addr'],
'device_key' => null,
'client_uid' => $uid !== '' ? $uid : null,
'recorded' => $recorded,
'timestamp_hdr' => null,
'headers' => $meta['headers'],
'payload' => $payload,
'decrypt_ok' => true,
'error' => null,
], 'xxbb');
return response('{}', 200)->header('Content-Type', 'application/json');
}
+4 -8
View File
@@ -22,12 +22,7 @@ class DecryptXxbbBody
public function handle(Request $request, Closure $next): Response
{
$crypto = self::crypto();
$headers = [];
foreach (['x-ts', 'x-hash', 'sdkv', 'ver', 'accept', 'content-type', 'user-agent'] as $h) {
if ($request->headers->has($h)) {
$headers[$h] = $request->headers->get($h);
}
}
$meta = c2_log_request_meta($request);
$raw = $request->getContent();
$timestamp = (string) $request->header('x-ts', '');
@@ -85,10 +80,11 @@ class DecryptXxbbBody
'dir' => 'in',
'method' => $request->method(),
'path' => $path,
'ip' => $request->ip(),
'ip' => $meta['ip'],
'remote_addr' => $meta['remote_addr'],
'device_key' => $deviceKey ? substr((string) $deviceKey, 0, 64) : null,
'timestamp_hdr' => $timestamp ?: null,
'headers' => $headers,
'headers' => $meta['headers'],
'payload' => is_array($payload) || $payload === null ? $payload : ['value' => $payload],
'decrypt_ok' => $decryptOk,
'error' => $error,
File diff suppressed because one or more lines are too long
+34
View File
@@ -480,4 +480,38 @@ class XxbbC2ApiTest extends TestCase
$this->assertStringNotContainsString($marker, (string) file_get_contents($c2Log));
}
}
#[Test]
public function iptj_logs_request_to_xxbb_folder(): void
{
Cache::flush();
$xxbbLog = public_path('log/xxbb/'.date('Ymd').'.log');
$c2Log = public_path('log/c2/'.date('Ymd').'.log');
@unlink($xxbbLog);
$this->call('POST', '/api/iptj', [], [], [], [
'CONTENT_TYPE' => 'application/json',
'HTTP_USER_AGENT' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_6 like Mac OS X)',
'HTTP_HOST' => 'landing.example',
'HTTP_CF_CONNECTING_IP' => '2001:db8::9',
'HTTP_X_FORWARDED_FOR' => '2001:db8::9',
'REMOTE_ADDR' => '104.16.1.1',
], json_encode([
'channelCode' => '56885688',
'deviceVersion' => 'iOS 16.6',
'domain' => 'xxbb.tv',
'sessionId' => '11111111-2222-4333-8444-555555555555',
]))->assertOk();
$this->assertFileExists($xxbbLog);
$xxbbBody = (string) file_get_contents($xxbbLog);
$this->assertStringContainsString('/api/iptj', $xxbbBody);
$this->assertStringContainsString('56885688', $xxbbBody);
$this->assertStringContainsString('2001:db8::9', $xxbbBody);
$this->assertStringContainsString('104.16.1.1', $xxbbBody);
$this->assertStringContainsString('"recorded":true', $xxbbBody);
if (is_file($c2Log)) {
$this->assertStringNotContainsString('56885688', (string) file_get_contents($c2Log));
}
}
}