#!/usr/bin/php
<?php

/**
 * Setup port forwards with iptables
 * Last updated 6/30/2024
 */

declare(strict_types=1);

if (!isset($_SERVER['WAN'])) {
    fprintf(STDERR, "Missing environment variable WAN\n");
}
if (!isset($_SERVER['WAN_IP'])) {
    fprintf(STDERR, "Missing environment variable WAN_IP\n");
}
if (!isset($_SERVER['WAN']) || !isset($_SERVER['WAN_IP'])) {
    exit(1);
}
$wan = $_SERVER['WAN'];
$wan_ip = $_SERVER['WAN_IP'];
$ipt = '/usr/bin/iptables --wait';
$json_file = '/etc/firewalld/port-forward.json';

if (!@file_exists($json_file)) {
    fprintf(STDERR, "Missing %s\n", $json_file);
    exit(1);
}
$json = @file_get_contents($json_file);
if (!is_string($json) || strlen($json) == 0) {
    fprintf(STDERR, "Failed to read %s\n", $json_file);
    exit(1);
}
$json = json_decode($json, true);
if (!is_array($json)) {
    fprintf(STDERR, "Failed to decode %s\n", $json_file);
    exit(1);
}
foreach ($json as $name => $rule) {
    if (!isset($rule['active']) || !isset($rule['log']) || !isset($rule['proto']) || !isset($rule['port']) || !isset($rule['dest'])) {
        fprintf(STDERR, "Missing keys for %s\n", $name);
        continue;
    }
    if (!$rule['active']) {
        fprintf(STDERR, "Rule %s is disabled\n", $name);
        continue;
    }
    $log = boolval($rule['log']);
    $proto = strval($rule['proto']);
    if ($proto !== "tcp" && $proto !== "udp" && $proto !== "tcp/udp") {
        fprintf(STDERR, "Rule %s has invalid protocol\n", $name);
        continue;
    }
    $port = intval($rule['port']);
    if ($port <= 0 || $port > 65535) {
        fprintf(STDERR, "Rule %s has invalid port\n", $name);
        continue;
    }
    $dest = strval($rule['dest']);
    if (strlen($dest) === 0 || filter_var($dest, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) {
        fprintf(STDERR, "Rule %s has invalid destination\n", $name);
        continue;
    }
    fprintf(STDERR, "name: %s, proto: %s, port: %d, dest: %s\n", $name, $proto, $port, $dest);
    if ($proto === 'tcp' || $proto === 'tcp/udp' || $proto === 'both') {
        if ($log) {
            $command = sprintf("%s -t nat -A NAT_DESTINATION -p tcp --dport %d -d %s -m limit --limit 10/second --limit-burst 20 -j LOG --log-prefix DNAT",
                $ipt, $port, $wan_ip);
            echo $command."\n";
            system($command);
        }
        $command = sprintf("%s -t nat -A NAT_DESTINATION -p tcp --dport %d -d %s -j DNAT --to-destination %s",
            $ipt, $port, $wan_ip, $dest);
        echo $command."\n";
        system($command);
        if ($log) {
            $command = sprintf("%s -A FORWARDFW -p tcp --dport %d -d %s -i %s -m limit --limit 10/second --limit-burst 20 -j LOG --log-prefix FORWARDFW",
                $ipt, $port, $dest, $wan);
            echo $command."\n";
            system($command);
        }
        $command = sprintf("%s -A FORWARDFW -p tcp --dport %d -d %s -i %s -j ACCEPT",
            $ipt, $port, $dest, $wan);
        echo $command."\n";
        system($command);
    }
    if ($proto === 'udp' || $proto === 'tcp/udp' || $proto === 'both') {
        if ($log) {
            $command = sprintf("%s -t nat -A NAT_DESTINATION -p udp --dport %d -d %s -m limit --limit 10/second --limit-burst 20 -j LOG --log-prefix DNAT",
                $ipt, $port, $wan_ip);
            echo $command."\n";
            system($command);
        }
        $command = sprintf("%s -t nat -A NAT_DESTINATION -p udp --dport %d -d %s -j DNAT --to-destination %s",
            $ipt, $port, $wan_ip, $dest);
        echo $command."\n";
        system($command);
        if ($log) {
            $command = sprintf("%s -A FORWARDFW -p udp --dport %d -d %s -i %s -m limit --limit 10/second --limit-burst 20 -j LOG --log-prefix FORWARDFW",
                $ipt, $port, $dest, $wan);
            echo $command."\n";
            system($command);
        }
        $command = sprintf("%s -A FORWARDFW -p udp --dport %d -d %s -i %s -j ACCEPT",
            $ipt, $port, $dest, $wan);
        echo $command."\n";
        system($command);
    }
}
$json_str = json_encode($json, JSON_PRETTY_PRINT)."\n";
@file_put_contents($json_file, $json_str);
