summaryrefslogtreecommitdiff
path: root/lib/nola/subnet.ex
blob: de469a6dab33f77717c0201afed3a2e00be5a136 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
defmodule Nola.Subnet do
  use Agent

  def start_link(_) do
    Agent.start_link(&setup/0, name: __MODULE__)
  end

  def assignations() do
    :dets.select(dets(), [{{:"$1", :"$2"}, [is_binary: :"$2"], [{{:"$1", :"$2"}}]}])
  end

  def find_subnet_for(binary) when is_binary(binary) do
    case :dets.select(dets(), [{{:"$1", :"$2"}, [{:==, :"$2", binary}], [{{:"$1", :"$2"}}]}]) do
      [{subnet, _}] -> subnet
      _ -> nil
    end
  end

  def assign(binary) when is_binary(binary) do
    result =
      if subnet = find_subnet_for(binary) do
        {:ok, subnet}
      else
        Agent.get_and_update(__MODULE__, fn dets ->
          {subnet, _} = available_select(dets)
          :dets.insert(dets, {subnet, binary})
          :dets.sync(dets)
          {{:new, subnet}, dets}
        end)
      end

    case result do
      {:new, subnet} ->
        ip = Pfx.host(subnet, 1)
        set_reverse(binary, ip)
        subnet

      {:ok, subnet} ->
        subnet
    end
  end

  def set_reverse(name, ip, value \\ nil)

  def set_reverse(name, ip, nil) do
    set_reverse(name, ip, "#{name}.users.goulag.org")
  end

  def set_reverse(_, ip, value) do
    ptr_zone = "3.0.0.2.d.f.0.a.2.ip6.arpa"
    ip_fqdn = Pfx.dns_ptr(ip)
    ip_local = String.replace(ip_fqdn, ".#{ptr_zone}", "")
    rev? = String.ends_with?(value, ".users.goulag.org")

    if rev? do
      {:ok, rev_zone} = PowerDNSex.show_zone("users.goulag.org")
      rev_update? = Enum.any?(rev_zone.rrsets, fn rr -> rr.name == "#{ip_fqdn}." end)

      record = %{
        name: "#{value}.",
        type: "AAAA",
        ttl: 8600,
        records: [%{content: ip, disabled: false}]
      }

      if(rev_update?,
        do: PowerDNSex.update_record(rev_zone, record),
        else: PowerDNSex.create_record(rev_zone, record)
      )
    end

    {:ok, zone} = PowerDNSex.show_zone(ptr_zone)
    update? = Enum.any?(zone.rrsets, fn rr -> rr.name == "#{ip_fqdn}." end)

    record = %{
      name: "#{ip_fqdn}.",
      type: "PTR",
      ttl: 3600,
      records: [%{content: "#{value}.", disabled: false}]
    }

    pdns =
      if(update?,
        do: PowerDNSex.update_record(zone, record),
        else: PowerDNSex.create_record(zone, record)
      )

    :ok
  end

  @doc false
  def dets() do
    (Nola.data_path() <> "/subnets.dets") |> String.to_charlist()
  end

  @doc false
  def setup() do
    {:ok, dets} = :dets.open_file(dets(), [])
    dets
  end

  defp available_select(dets) do
    spec = [{{:"$1", :"$2"}, [is_integer: :"$2"], [{{:"$1", :"$2"}}]}]
    {subnets, _} = :dets.select(dets, spec, 20)

    subnet =
      subnets
      |> Enum.sort_by(fn {_, last} -> last end)
      |> List.first()
  end
end