using Lampac.Engine; using Lampac.Engine.CRON; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.SignalR; using Microsoft.CodeAnalysis; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyModel; using Microsoft.Extensions.Hosting; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Shared; using Shared.Engine; using Shared.Models.Base; using Shared.Models.SISI.Base; using Shared.Models.SQL; using Shared.PlaywrightCore; using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.Loader; using System.Text; using System.Text.RegularExpressions; using System.Threading; namespace Lampac { public class Program { #region static public static string Runtime { get; private set; } public static bool RuntimeCve2025_55315 { get; private set; } public static AppReload appReload { get; private set; } public static List assemblieReferences { get; private set; } static IHost _host; public static bool _reload { get; private set; } = true; public static List<(IPAddress prefix, int prefixLength)> cloudflare_ips = new List<(IPAddress prefix, int prefixLength)>(); static Timer _usersTimer, _kitTimer; #endregion #region Main public static void Main(string[] args) { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); bool IsAssemblyLoaded(AssemblyName assemblyName) { foreach (var assembly in assemblies) { if (assembly.GetName().Name == assemblyName.Name) return true; } return false; } foreach (string dllPath in Directory.GetFiles(Path.Combine(AppContext.BaseDirectory, "runtimes", "references"), "*.dll")) { try { AssemblyName assemblyName = AssemblyName.GetAssemblyName(dllPath); if (!IsAssemblyLoaded(assemblyName)) Assembly.LoadFrom(dllPath); } catch (Exception ex) { Console.WriteLine($"Failed to load {dllPath}: {ex.Message}"); } } AssemblyLoadContext.Default.Resolving += (context, assemblyName) => { foreach (string name in new string[] { $"ru/{assemblyName.Name}", assemblyName.Name }) { string assemblyPath = Path.Combine(AppContext.BaseDirectory, "runtimes", "references", name); if (File.Exists(assemblyPath)) return context.LoadFromAssemblyPath(assemblyPath); } return null; }; Run(args); } #endregion #region Run static void Run(string[] args) { #region assemblieReferences var dependencyContext = DependencyContext.Default; var assemblies = dependencyContext.RuntimeLibraries .SelectMany(library => library.GetDefaultAssemblyNames(dependencyContext)) .Select(Assembly.Load) .ToList(); assemblieReferences = assemblies.Select(assembly => MetadataReference.CreateFromFile(assembly.Location)).ToList(); #endregion #region dotnet runtime var asm = typeof(WebApplication).Assembly; string info = asm.GetCustomAttribute()?.InformationalVersion; if (!string.IsNullOrWhiteSpace(info)) { Runtime = info.Split('+', '-', ' ')[0]; RuntimeCve2025_55315 = Runtime is "9.0.1" or "9.0.2" or "9.0.3" or "9.0.4" or "9.0.5" or "9.0.6" or "9.0.7" or "9.0.8" or "9.0.9"; } #endregion CultureInfo.CurrentCulture = new CultureInfo("ru-RU"); Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var init = AppInit.conf; var mods = init.BaseModule; #region GC var gc = init.GC; if (gc != null && gc.enable) { if (gc.Concurrent.HasValue) AppContext.SetSwitch("System.GC.Concurrent", gc.Concurrent.Value); if (gc.ConserveMemory.HasValue) AppContext.SetData("System.GC.ConserveMemory", gc.ConserveMemory.Value); if (gc.HighMemoryPercent.HasValue) AppContext.SetData("System.GC.HighMemoryPercent", gc.HighMemoryPercent.Value); if (gc.RetainVM.HasValue) AppContext.SetSwitch("System.GC.RetainVM", gc.RetainVM.Value); } #endregion #region Http.onlog Http.onlog += (e, log) => { if (mods.nws) NativeWebSocket.SendLog(log, "http"); if (mods.ws) soks.SendLog(log, "http"); }; #endregion #region RchClient RchClient.hub += (e, req) => { if (mods.nws) _ = NativeWebSocket.SendRchRequestAsync(req.connectionId, req.rchId, req.url, req.data, req.headers, req.returnHeaders).ConfigureAwait(false); if (mods.ws) _ = soks.hubClients?.Client(req.connectionId)?.SendAsync("RchClient", req.rchId, req.url, req.data, req.headers, req.returnHeaders)?.ConfigureAwait(false); }; #endregion #region current.conf if (!AppInit.conf.multiaccess) { Console.WriteLine(JsonConvert.SerializeObject(AppInit.conf, Formatting.Indented, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore })); } File.WriteAllText("current.conf", JsonConvert.SerializeObject(AppInit.conf, Formatting.Indented)); #endregion ThreadPool.GetMinThreads(out int workerThreads, out int completionPortThreads); ThreadPool.SetMinThreads(Math.Max(4096, workerThreads), Math.Max(1024, completionPortThreads)); #region passwd if (!File.Exists("passwd")) { AppInit.rootPasswd = Guid.NewGuid().ToString(); File.WriteAllText("passwd", AppInit.rootPasswd); } else { AppInit.rootPasswd = File.ReadAllText("passwd"); AppInit.rootPasswd = Regex.Replace(AppInit.rootPasswd, "[\n\r\t ]+", "").Trim(); } #endregion #region vers.txt if (!File.Exists("data/vers.txt")) File.WriteAllText("data/vers.txt", BaseController.appversion); if (!File.Exists("data/vers-minor.txt")) File.WriteAllText("data/vers-minor.txt", "1"); #endregion #region SQL HybridCacheContext.Initialization(); ProxyLinkContext.Initialization(); if (init.chromium.enable || init.firefox.enable) PlaywrightContext.Initialization(); if (mods.Sql.externalids) ExternalidsContext.Initialization(); if (mods.Sql.sisi) SisiContext.Initialization(); if (mods.Sql.syncUser) SyncUserContext.Initialization(); #endregion #region migration if (Directory.Exists("cache/storage") || Directory.Exists("cache/bookmarks/sisi")) { Console.WriteLine("run migration"); #region cache/storage if (mods.Sql.syncUser && Directory.Exists("cache/storage")) { string sourceDir = "cache/storage"; string targetDir = "database/storage"; void CopyAll(string source, string target) { Directory.CreateDirectory(target); foreach (string file in Directory.GetFiles(source)) { string destFile = Path.Combine(target, Path.GetFileName(file)); File.Copy(file, destFile, true); } foreach (string dir in Directory.GetDirectories(source)) { string destDir = Path.Combine(target, Path.GetFileName(dir)); CopyAll(dir, destDir); } } CopyAll(sourceDir, targetDir); Directory.Move("cache/storage", "cache/storage.bak"); } #endregion #region cache/bookmarks/sisi if (mods.Sql.sisi && Directory.Exists("cache/bookmarks/sisi")) { using (var sqlDb = new SisiContext()) { var existing = new HashSet( sqlDb.bookmarks .AsNoTracking() .Select(i => $"{i.user}:{i.uid}") ); foreach (string folder in Directory.GetDirectories("cache/bookmarks/sisi")) { string folderName = Path.GetFileName(folder); foreach (string file in Directory.GetFiles(folder)) { try { string md5user = folderName + Path.GetFileName(file); var bookmarks = JsonConvert.DeserializeObject>(File.ReadAllText(file)); if (bookmarks == null || bookmarks.Count == 0) continue; DateTime now = DateTime.UtcNow; for (int i = 0; i < bookmarks.Count; i++) { var pl = bookmarks[i]; if (pl?.bookmark == null || string.IsNullOrEmpty(pl.bookmark.uid)) continue; if (!existing.Add($"{md5user}:{pl.bookmark.uid}")) continue; sqlDb.bookmarks.Add(new SisiBookmarkSqlModel { user = md5user, uid = pl.bookmark.uid, created = now.AddSeconds(-i), json = JsonConvert.SerializeObject(pl), name = pl.name, model = pl.model?.name }); } } catch { } } } sqlDb.SaveChanges(); } Directory.Move("cache/bookmarks/sisi", "cache/bookmarks/sisi.bak"); } #endregion } #endregion #region Playwright if (init.chromium.enable || init.firefox.enable) { if (!init.multiaccess) Environment.SetEnvironmentVariable("NODE_OPTIONS", "--max-old-space-size=256"); ThreadPool.QueueUserWorkItem(async _ => { if (await PlaywrightBase.InitializationAsync()) { if (init.chromium.enable) _ = Chromium.CreateAsync().ConfigureAwait(false); if (init.firefox.enable) _ = Firefox.CreateAsync().ConfigureAwait(false); } }); Chromium.CronStart(); Firefox.CronStart(); } #endregion #region cloudflare_ips ThreadPool.QueueUserWorkItem(async _ => { string ips = await Http.Get("https://www.cloudflare.com/ips-v4"); if (ips == null || !ips.Contains("173.245.")) ips = File.Exists("data/cloudflare/ips-v4.txt") ? File.ReadAllText("data/cloudflare/ips-v4.txt") : null; if (ips != null) { string ips_v6 = await Http.Get("https://www.cloudflare.com/ips-v6"); if (ips_v6 == null || !ips_v6.Contains("2400:cb00")) ips_v6 = File.Exists("data/cloudflare/ips-v6.txt") ? File.ReadAllText("data/cloudflare/ips-v6.txt") : null; if (ips_v6 != null) { foreach (string ip in (ips + "\n" + ips_v6).Split('\n')) { if (string.IsNullOrEmpty(ip) || !ip.Contains("/")) continue; try { string[] ln = ip.Split('/'); cloudflare_ips.Add((IPAddress.Parse(ln[0].Trim()), int.Parse(ln[1].Trim()))); } catch { } } } } Console.WriteLine($"cloudflare_ips: {cloudflare_ips.Count}"); }); #endregion #region fix update.sh if (File.Exists("update.sh")) { var olds = new string[] { "02a7e97392e63b7e9e35a39ce475d6f8", "6354eab8b101af90cb247fc8c977dd6b", "b94b42ff158682661761a0b50a808a3b", "97b0d657786b14e6a2faf7186de0556c", "6b60a4d2173e99b11ecf4e792a24f598", "cae6f0e79bbb2e6832922f25614d83a1", "97b0d657786b14e6a2faf7186de0556c", "cae6f0e79bbb2e6832922f25614d83a1", "587794ca93c8d0318332858cf0e71e98", "174ac2b94c5aa0e5ac086f843fd086a6", "9c258d50e9eb06316efdf33de8b66dc3", "bb4d6f2ba74b6a25dc3e4638c7f5282a", "9607ae5805eaf5d06220298581a99beb", "30078b973188c696273e10d6ef0ebbb2", "92f5e2e03d2cc2697f2ee00becdb4696", "b565c7e163485b8f8cc258b95f2891b6", "ec6659f1f91f1f6ec0c734ff2111c7d7", "43c8f2a9fe75a3ce9c109e67bc552ace", "1ba06699c494190bce44f6786a24b96a" }; try { if (olds.Contains(CrypTo.md5(File.ReadAllText("update.sh")))) { ThreadPool.QueueUserWorkItem(async _ => { string new_update = await Http.Get("https://raw.githubusercontent.com/immisterio/Lampac/refs/heads/main/update.sh"); if (new_update != null && new_update.Contains("DEST=\"/home/lampac\"")) File.WriteAllText("update.sh", new_update); }); } } catch { } } #endregion CacheCron.Run(); if (mods.kurwaCron) KurwaCron.Run(); PluginsCron.Run(); SyncCron.Run(); if (AppInit.modules?.FirstOrDefault(i => i.dll == "DLNA.dll" && i.enable) != null) TrackersCron.Run(); LampaCron.Run(); appReload = new AppReload(); appReload.InkvReload = () => { _host.StopAsync(); AppInit.LoadModules(); }; _usersTimer = new Timer(UpdateUsersDb, null, TimeSpan.Zero, TimeSpan.FromSeconds(1)); _kitTimer = new Timer(UpdateKitDb, null, TimeSpan.Zero, TimeSpan.FromSeconds(Math.Max(5, AppInit.conf.kit.cacheToSeconds))); while (_reload) { _host = CreateHostBuilder(args).Build(); _reload = false; _host.Run(); } } #endregion #region CreateHostBuilder public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseKestrel(op => { op.AddServerHeader = false; if (AppInit.conf.listen.keepalive.HasValue && AppInit.conf.listen.keepalive.Value > 0) op.Limits.KeepAliveTimeout = TimeSpan.FromSeconds(AppInit.conf.listen.keepalive.Value); op.ConfigureEndpointDefaults(endpointOptions => { if (AppInit.conf.listen.endpointDefaultsProtocols.HasValue) endpointOptions.Protocols = AppInit.conf.listen.endpointDefaultsProtocols.Value; }); if (string.IsNullOrEmpty(AppInit.conf.listen.sock) && string.IsNullOrEmpty(AppInit.conf.listen.ip)) { op.Listen(IPAddress.Parse("127.0.0.1"), 9118); } else { if (!string.IsNullOrEmpty(AppInit.conf.listen.sock)) { if (File.Exists($"/var/run/{AppInit.conf.listen.sock}.sock")) File.Delete($"/var/run/{AppInit.conf.listen.sock}.sock"); op.ListenUnixSocket($"/var/run/{AppInit.conf.listen.sock}.sock"); } if (!string.IsNullOrEmpty(AppInit.conf.listen.ip)) op.Listen(AppInit.conf.listen.ip == "any" ? IPAddress.Any : AppInit.conf.listen.ip == "broadcast" ? IPAddress.Broadcast : IPAddress.Parse(AppInit.conf.listen.ip), AppInit.conf.listen.port); } }) .UseStartup(); }); #endregion #region UpdateUsersDb static int _updateUsersDb = 0; static string _usersKeyUpdate = string.Empty; static void UpdateUsersDb(object state) { if (Interlocked.Exchange(ref _updateUsersDb, 1) == 1) return; try { if (File.Exists("users.json")) { var lastWriteTime = File.GetLastWriteTime("users.json"); string keyUpdate = $"{AppInit.conf?.guid}:{AppInit.conf?.accsdb?.users?.Count ?? 0}:{lastWriteTime}"; if (keyUpdate == _usersKeyUpdate) return; foreach (var user in JsonConvert.DeserializeObject>(File.ReadAllText("users.json"))) { try { var find = AppInit.conf.accsdb.findUser(user.id ?? user.ids?.First()); if (find != null) { find.id = user.id; find.ids = user.ids; find.group = user.group; find.IsPasswd = user.IsPasswd; find.expires = user.expires; find.ban = user.ban; find.ban_msg = user.ban_msg; find.comment = user.comment; find.@params = user.@params; } else { AppInit.conf.accsdb.users.Add(user); } } catch { } } _usersKeyUpdate = keyUpdate; } } catch { } finally { Volatile.Write(ref _updateUsersDb, 0); } } #endregion #region UpdateKitDb static int _updateKitDb = 0; async static void UpdateKitDb(object state) { if (Interlocked.Exchange(ref _updateKitDb, 1) == 1) return; try { if (AppInit.conf.kit.enable && AppInit.conf.kit.IsAllUsersPath && !string.IsNullOrEmpty(AppInit.conf.kit.path)) { var users = await Http.Get>(AppInit.conf.kit.path); if (users != null) AppInit.conf.kit.allUsers = users; } } catch { } finally { Volatile.Write(ref _updateKitDb, 0); } } #endregion } }