refactor: 枚举移入Models目录,命名空间更新为Rainbow.Entity.Models
大石头 authored at 2026-07-02 12:54:58
3.49 KiB
RainbowBridge
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NewLife;
using NewLife.Log;
using NewLife.Threading;
using Rainbow.Entity;

namespace Rainbow.Services;

/// <summary>防火墙/NAT 管理器。支持时段策略</summary>
public class FirewallManager
{
    public ShellExecutor Shell { get; set; }
    public ConfigBackup Backup { get; set; }
    public ILog Log { get; set; } = Logger.Null;
    private readonly IFirewallAdapter? _adapter;
    private TimerX _scheduleTimer;
    private readonly ConcurrentDictionary<Int32, (String StartTime, String EndTime, Int32 WeekDays)> _schedules = new();
    private Int32 _scheduling;

    public FirewallManager(ShellExecutor shell = null, ConfigBackup backup = null, IFirewallAdapter? adapter = null) { Shell = shell; Backup = backup; _adapter = adapter; }

    public void SetSchedule(Int32 ruleId, String startTime, String endTime, Int32 weekDays = 127)
    {
        if (startTime.IsNullOrEmpty() || endTime.IsNullOrEmpty()) { _schedules.TryRemove(ruleId, out _); return; }
        _schedules[ruleId] = (startTime, endTime, weekDays);
        StartScheduleTimer();
        Log.Info("规则[{0}] 时段设置:{1}-{2}", ruleId, startTime, endTime);
    }

    public void RemoveSchedule(Int32 ruleId) { _schedules.TryRemove(ruleId, out _); }

    private void StartScheduleTimer()
    {
        if (_scheduleTimer != null) return;
        _scheduleTimer = new TimerX(OnScheduleCheck, null, 10000, 60_000, "Firewall") { Async = true };
    }

    private void OnScheduleCheck(Object state)
    {
        if (Interlocked.CompareExchange(ref _scheduling, 1, 0) != 0) return;
        try
        {
            var now = DateTime.Now;
            var currentTime = now.ToString("HH:mm");
            var dayOfWeek = (Int32)now.DayOfWeek;
            var dayBit = dayOfWeek == 0 ? 64 : 1 << (dayOfWeek - 1);
            foreach (var kv in _schedules)
            {
                var (startTime, endTime, weekDays) = kv.Value;
                var inRange = String.Compare(currentTime, startTime, StringComparison.Ordinal) >= 0 && String.Compare(currentTime, endTime, StringComparison.Ordinal) <= 0;
                var shouldEnable = (weekDays & dayBit) != 0 && inRange;
                var rule = FirewallRule.FindById(kv.Key);
                if (rule == null) { _schedules.TryRemove(kv.Key, out _); continue; }
                if (rule.Enable != shouldEnable) { rule.Enable = shouldEnable; rule.Update(); Log.Info("规则[{0}] 时段调度:{1}", kv.Key, shouldEnable ? "启用" : "停用"); }
            }
        }
        catch (Exception ex) { Log.Error("时段调度异常:{0}", ex.Message); }
        finally { Interlocked.Exchange(ref _scheduling, 0); }
    }

    public IReadOnlyDictionary<Int32, (String StartTime, String EndTime, Int32 WeekDays)> GetSchedules() => _schedules;

    public async Task<ShellResult> SaveRulesAsync()
    {
        if (_adapter != null)
        {
            var ok = await _adapter.SaveAsync();
            return new ShellResult { ExitCode = ok ? 0 : -1, Stdout = ok ? "防火墙规则已保存" : null, Stderr = ok ? null : "保存防火墙规则失败" };
        }

        // 无适配器时回退到 shell 命令(Linux iptables-save)
        if (Shell != null)
        {
            var result = await Shell.ExecuteAsync("iptables-save", "", true);
            return result;
        }

        return ShellResult.Fail("无可用防火墙适配器");
    }
}