mirror of
https://github.com/myawired/2025-RecRoom-Server-E12354.git
synced 2026-07-17 16:11:15 +10:00
Initial upload
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
using Rec_rewild_live_rewrite.api.server;
|
||||
|
||||
public class IPAllowMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
private static readonly HashSet<string> _allowedIPs = new()
|
||||
{
|
||||
"47.198.33.69"
|
||||
//"66.51.113.175",
|
||||
//"96.27.234.110"
|
||||
};
|
||||
|
||||
public static bool IPAllowMiddleware_enable = true;
|
||||
|
||||
public IPAllowMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
if (!IPAllowMiddleware_enable)
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
string? remoteIpString = context.Request.Headers["CF-Connecting-IP"].FirstOrDefault() ?? context.Request.Headers["X-Forwarded-For"].FirstOrDefault() ?? context.Connection.RemoteIpAddress?.ToString();
|
||||
|
||||
string? useragent = context.Request.Headers["User-Agent"].FirstOrDefault();
|
||||
|
||||
if (string.IsNullOrEmpty(remoteIpString))
|
||||
{
|
||||
Deny(context, remoteIpString, useragent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!System.Net.IPAddress.TryParse(remoteIpString, out var remoteIp))
|
||||
{
|
||||
Deny(context, remoteIpString, useragent);
|
||||
return;
|
||||
}
|
||||
|
||||
if (System.Net.IPAddress.IsLoopback(remoteIp))
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
if (remoteIp.IsIPv4MappedToIPv6)
|
||||
{
|
||||
remoteIp = remoteIp.MapToIPv4();
|
||||
}
|
||||
|
||||
if (!_allowedIPs.Contains(remoteIp.ToString()))
|
||||
{
|
||||
await Deny(context, remoteIp.ToString(), useragent);
|
||||
return;
|
||||
}
|
||||
|
||||
await _next(context);
|
||||
}
|
||||
|
||||
private static async Task Deny(HttpContext context, string? ip, string? useragent)
|
||||
{
|
||||
string msg = $"Forbidden request from IP: {ip} and user agent {useragent}";
|
||||
Console.WriteLine($"[IPAllowMiddleware] {msg}");
|
||||
Webhook.Send(1, "gaylog", "uh oh!", msg, 1);
|
||||
if (!context.Response.HasStarted)
|
||||
{
|
||||
context.Response.Clear();
|
||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
await context.Response.WriteAsync(
|
||||
""
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
public class IPBlockMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private static readonly HashSet<string> _blockedIPs = new() // this can be ipv6 or ipv4
|
||||
{
|
||||
"37.203.37.86",
|
||||
"173.2.187.39"
|
||||
};
|
||||
|
||||
public IPBlockMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
string? remoteIp = context.Request.Headers["CF-Connecting-IP"].FirstOrDefault()
|
||||
?? context.Request.Headers["X-Forwarded-For"].FirstOrDefault()
|
||||
?? context.Connection.RemoteIpAddress?.ToString();
|
||||
|
||||
if (!string.IsNullOrEmpty(remoteIp) && _blockedIPs.Contains(remoteIp))
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;// this do kick people //Status401Unauthorized;
|
||||
await context.Response.WriteAsync("");
|
||||
return;
|
||||
}
|
||||
|
||||
await _next(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
public class Log
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly string _logFilePath;
|
||||
|
||||
public Log(RequestDelegate next, string logFilePath)
|
||||
{
|
||||
_next = next;
|
||||
_logFilePath = logFilePath;
|
||||
|
||||
try
|
||||
{
|
||||
var dir = Path.GetDirectoryName(_logFilePath);
|
||||
if (!string.IsNullOrEmpty(dir))
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// even startup logging must never crash
|
||||
}
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
await _next(context);
|
||||
|
||||
try
|
||||
{
|
||||
var logLine =
|
||||
$"{DateTime.UtcNow:o} {context.Request.Method} {context.Request.Path} -> {context.Response.StatusCode}";
|
||||
|
||||
await File.AppendAllTextAsync(
|
||||
_logFilePath,
|
||||
logLine + Environment.NewLine
|
||||
);
|
||||
|
||||
}
|
||||
catch
|
||||
{
|
||||
// NEVER let logging crash the request
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public class RequestResponseLoggingMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
private readonly ILogger<RequestResponseLoggingMiddleware> _logger;
|
||||
|
||||
public RequestResponseLoggingMiddleware(RequestDelegate next, ILogger<RequestResponseLoggingMiddleware> logger)
|
||||
{
|
||||
_next = next;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
if (context.Request.Path.Equals("/api/images/v4/uploadsaved") || (context.Request.Path.Equals("/Matchmaking/heartbeat") || context.Request.Path.Equals("/api/gameconfigs/v1/all") || context.Request.Path.StartsWithSegments("/Room_server/rooms/visitedby/me") || context.Request.Path.Equals("/api/avatar/v4/items") || context.Request.Path.Equals("/api/avatar/v1/defaultunlocked") || context.Request.Path.Equals("/api/equipment/v2/getUnlocked") || context.Request.Path.Equals("/admin/api/players") || context.Request.Path.Equals("/admin/api/rooms") || context.Request.Path.StartsWithSegments("/Room_server/rooms/hot") || context.Request.Path.StartsWithSegments("/Room_server/rooms/") || context.Request.Path.StartsWithSegments("/Room_server/rooms/search") || context.Request.Path.StartsWithSegments("/api/images/v5") || context.Request.Path.StartsWithSegments("/cdn/room") || context.Request.Path.StartsWithSegments("/cdn/") || context.Request.Path.Equals("/api/consumables/v2/getUnlocked")))
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
|
||||
string requestUrl = $"{context.Request.Method} {context.Request.Scheme}://{context.Request.Host}{context.Request.Path}{context.Request.QueryString}";
|
||||
string contentType = context.Request.ContentType ?? string.Empty;
|
||||
bool isBinaryRequest = IsBinaryContentType(contentType);
|
||||
|
||||
string cfConnectingIp = context.Request.Headers["CF-Connecting-IP"].FirstOrDefault() ?? "[NO CF-CONNECTING-IP]";
|
||||
|
||||
string auth = context.Request.Headers["Authorization"].FirstOrDefault()
|
||||
?? context.Request.Cookies["Authorization"];
|
||||
|
||||
|
||||
string requestBody = (context.Request.Method == "POST" || context.Request.Method == "PUT") && !isBinaryRequest
|
||||
? await ReadRequestBody(context)
|
||||
: "[NO REQUEST BODY]";
|
||||
|
||||
Console.WriteLine($"[REQUEST] {requestUrl}");
|
||||
Console.WriteLine($"[CF-CONNECTING-IP] {cfConnectingIp}");
|
||||
Console.WriteLine($"[AUTH] {auth}");
|
||||
|
||||
_logger.LogInformation($"Request: {requestUrl} - CF-Connecting-IP: {cfConnectingIp}");
|
||||
|
||||
if (!isBinaryRequest)
|
||||
{
|
||||
Console.WriteLine($"[REQUEST BODY] {requestBody}");
|
||||
_logger.LogInformation($"Request Body: {requestBody}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("[REQUEST BODY] [BINARY CONTENT SKIPPED]");
|
||||
}
|
||||
|
||||
var originalResponseBodyStream = context.Response.Body;
|
||||
using (var responseBodyStream = new MemoryStream())
|
||||
{
|
||||
context.Response.Body = responseBodyStream;
|
||||
await _next(context);
|
||||
|
||||
string responseContentType = context.Response.ContentType ?? string.Empty;
|
||||
bool isBinaryResponse = IsBinaryContentType(responseContentType);
|
||||
|
||||
string responseBody = !isBinaryResponse ? await ReadResponseBody(responseBodyStream) : "[BINARY CONTENT SKIPPED]";
|
||||
|
||||
Console.WriteLine($"[RESPONSE] Status: {context.Response.StatusCode}");
|
||||
if (!isBinaryResponse)
|
||||
{
|
||||
Console.WriteLine($"[RESPONSE BODY] {responseBody}");
|
||||
_logger.LogInformation($"Response: {context.Response.StatusCode} - Body: {responseBody}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("[RESPONSE BODY] [BINARY CONTENT SKIPPED]");
|
||||
}
|
||||
|
||||
responseBodyStream.Seek(0, SeekOrigin.Begin);
|
||||
await responseBodyStream.CopyToAsync(originalResponseBodyStream);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<string> ReadRequestBody(HttpContext context)
|
||||
{
|
||||
context.Request.EnableBuffering();
|
||||
|
||||
using var reader = new StreamReader(context.Request.Body, Encoding.UTF8, leaveOpen: true);
|
||||
string body = await reader.ReadToEndAsync();
|
||||
context.Request.Body.Position = 0;
|
||||
|
||||
return string.IsNullOrWhiteSpace(body) ? "[EMPTY REQUEST BODY]" : body;
|
||||
}
|
||||
|
||||
private async Task<string> ReadResponseBody(Stream responseBodyStream)
|
||||
{
|
||||
responseBodyStream.Seek(0, SeekOrigin.Begin);
|
||||
using var reader = new StreamReader(responseBodyStream, Encoding.UTF8, leaveOpen: true);
|
||||
string body = await reader.ReadToEndAsync();
|
||||
responseBodyStream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return string.IsNullOrWhiteSpace(body) ? "[EMPTY RESPONSE BODY]" : body;
|
||||
}
|
||||
|
||||
private bool IsBinaryContentType(string contentType)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(contentType))
|
||||
return false;
|
||||
|
||||
contentType = contentType.ToLowerInvariant();
|
||||
|
||||
return contentType.StartsWith("image/") ||
|
||||
contentType.StartsWith("audio/") ||
|
||||
contentType.StartsWith("video/") ||
|
||||
contentType == "application/octet-stream" ||
|
||||
contentType.Contains("application/pdf") ||
|
||||
contentType.Contains("application/zip") ||
|
||||
contentType.Contains("application/x-binary");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public class VpnBlockMiddleware
|
||||
{
|
||||
private static readonly HttpClient Http = new HttpClient
|
||||
{
|
||||
Timeout = TimeSpan.FromSeconds(5)
|
||||
};
|
||||
|
||||
private static readonly string DataPath = Path.Combine(Environment.CurrentDirectory, "Data");
|
||||
private static readonly string CacheFile = Path.Combine(DataPath, "Ips.json");
|
||||
|
||||
private static readonly List<IpCacheEntry> IpCache;
|
||||
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
static VpnBlockMiddleware()
|
||||
{
|
||||
Http.DefaultRequestHeaders.UserAgent.ParseAdd(
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
|
||||
|
||||
// Ensure Data folder exists
|
||||
if (!Directory.Exists(DataPath))
|
||||
Directory.CreateDirectory(DataPath);
|
||||
|
||||
// Load or initialize cache
|
||||
if (File.Exists(CacheFile))
|
||||
{
|
||||
var json = File.ReadAllText(CacheFile);
|
||||
try
|
||||
{
|
||||
IpCache = JsonSerializer.Deserialize<List<IpCacheEntry>>(json) ?? new List<IpCacheEntry>();
|
||||
}
|
||||
catch
|
||||
{
|
||||
IpCache = new List<IpCacheEntry>();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IpCache = new List<IpCacheEntry>();
|
||||
}
|
||||
}
|
||||
|
||||
public VpnBlockMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
string? ip = context.Request.Headers["CF-Connecting-IP"].FirstOrDefault()
|
||||
?? context.Request.Headers["X-Forwarded-For"].FirstOrDefault()
|
||||
?? context.Connection.RemoteIpAddress?.ToString();
|
||||
|
||||
if (ip == null)
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
await context.Response.WriteAsync("???");
|
||||
return;
|
||||
}
|
||||
|
||||
string ipStr = ip.ToString();
|
||||
|
||||
// ---------- VPN CHECK START ----------
|
||||
|
||||
// Check cache first
|
||||
var cached = IpCache.Find(x => x.Ip == ipStr);
|
||||
if (cached != null)
|
||||
{
|
||||
if (cached.IsVpn)
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
await context.Response.WriteAsync("You have been blocked due to being suspicious.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
await _next(context);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Not in cache fetch from web
|
||||
bool isVpn = true;
|
||||
try
|
||||
{
|
||||
var url = $"https://whatismyipaddress.com/ip/{ipStr}";
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
|
||||
// Set all headers to mimic real browser
|
||||
request.Headers.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7");
|
||||
request.Headers.Add("Accept-Encoding", "gzip, deflate, br, zstd");
|
||||
request.Headers.Add("Accept-Language", "en-US,en;q=0.9");
|
||||
request.Headers.Add("Cache-Control", "no-cache");
|
||||
request.Headers.Add("Cookie", "_omappvp=gVm5QIhYs7ZPribnnBc5i0hcLYpksUDiKmaUjnC3eJDngY1GqHDK9X9VZ6X6YjdGJOIEopJi4PrOdRMf9FdpCTgsbQBQd8NA; usprivacy=1N--; pt=3f72ce96ed0863ed93f8e4f35cd5c051; cf_clearance=E.lrd8juQIWkUqgGOEu9IK1D7hj4VjU9MrKMu3ndw.Q-1768983651-1.2.1.1-yatNw.jMPyw7hUdlaVTRBwLPNS3zrTyJYuJP7WZ5cMC_JU1QcPOxxCSj.BIc6n9gYQlDTuu3MIeDohOS.dF.GzEY6SroaJJRS3Kqz.bSs2HmGA8WiDdMZ585PG47Sngr2MFufhyaQUejsCs62g3Xn5TNQW6ScnFH.SwZ_4_ltZrdvu7pYtu3SrGQC8rWvhyHgR_6DJJ8BM64SeF8zIL9ghgA4e0cSmp3q63JijsJb_I; _awl=2.1768987486.5-5aef88a3ad28e1513eae2c6b9b05ea1e-6763652d75732d7765737431-1");
|
||||
request.Headers.Add("Pragma", "no-cache");
|
||||
request.Headers.Add("Priority", "u=0, i");
|
||||
request.Headers.Add("Sec-CH-UA", "\"Google Chrome\";v=\"143\", \"Chromium\";v=\"143\", \"Not A(Brand\";v=\"24\"");
|
||||
request.Headers.Add("Sec-CH-UA-Mobile", "?0");
|
||||
request.Headers.Add("Sec-CH-UA-Platform", "\"Windows\"");
|
||||
request.Headers.Add("Sec-Fetch-Dest", "document");
|
||||
request.Headers.Add("Sec-Fetch-Mode", "navigate");
|
||||
request.Headers.Add("Sec-Fetch-Site", "none");
|
||||
request.Headers.Add("Sec-Fetch-User", "?1");
|
||||
request.Headers.Add("Upgrade-Insecure-Requests", "1");
|
||||
request.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36");
|
||||
|
||||
var response = await Http.SendAsync(request);
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Console.WriteLine($"[VPNBlockMiddleware] Failed to fetch IP info {ipStr}, status {response.StatusCode}");
|
||||
isVpn = true; // fail-closed
|
||||
}
|
||||
else
|
||||
{
|
||||
var html = await response.Content.ReadAsStringAsync();
|
||||
isVpn = !html.Contains("Services:</span><span>None detected", StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"[VPNBlockMiddleware] Error checking IP {ipStr}: {ex.Message}");
|
||||
isVpn = false; // fail-closed
|
||||
}
|
||||
|
||||
// Save to cache
|
||||
IpCache.Add(new IpCacheEntry { Ip = ipStr, IsVpn = isVpn });
|
||||
File.WriteAllText(CacheFile, JsonSerializer.Serialize(IpCache, new JsonSerializerOptions { WriteIndented = false }));
|
||||
|
||||
if (isVpn)
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
await context.Response.WriteAsync("You have been blocked due to being suspicious.");
|
||||
return;
|
||||
}
|
||||
|
||||
// ---------- VPN CHECK END ----------
|
||||
|
||||
await _next(context);
|
||||
}
|
||||
|
||||
private class IpCacheEntry
|
||||
{
|
||||
public string Ip { get; set; } = "";
|
||||
public bool IsVpn { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user