feat: xxbb
This commit is contained in:
@@ -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');
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user