Skip to content

Commit

Permalink
Add support for Busybox dnsd
Browse files Browse the repository at this point in the history
  • Loading branch information
fhunleth committed Oct 11, 2019
1 parent 5f91ee1 commit a1e89a4
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 1 deletion.
129 changes: 129 additions & 0 deletions lib/vintage_net/ip/dnsd_config.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
defmodule VintageNet.IP.DnsdConfig do
@moduledoc """
This is a helper module for VintageNet.Technology implementations that use
the Busybox DNS server.
DNS functionality is only supported for IPv4 configurations using static IP
addresses.
DNS server parameters are:
* `:port` - The port to use (defaults to 53)
* `:ttl` - DNS record TTL in seconds (defaults to 120)
* `:records` - DNS A records (required)
The `:records` option is a list of name/IP address tuples. For example:
```
[{"example.com", {1, 2, 3, 4}}]
```
Only IPv4 addresses are supported. Addresses may be specified as strings or
tuples, but will be normalized to tuple form before being applied.
"""

alias VintageNet.{Command, IP}
alias VintageNet.Interface.RawConfig

@doc """
Normalize the DNSD parameters in a configuration.
"""
@spec normalize(map()) :: map()
def normalize(%{ipv4: %{method: :static}, dnsd: dnsd} = config) do
# Normalize IP addresses
new_dnsd =
dnsd
|> Map.update(:records, [], &normalize_records/1)
|> Map.take([
:records,
:port,
:ttl
])

%{config | dnsd: new_dnsd}
end

def normalize(%{dnsd: _something_else} = config) do
# DNSD won't be started if not an IPv4 static configuration
Map.drop(config, [:dnsd])
end

def normalize(config), do: config

defp normalize_records(records) do
Enum.map(records, &normalize_record/1)
end

defp normalize_record({name, ipa}) do
{name, IP.ip_to_tuple!(ipa)}
end

@doc """
Add dnsd configuration commands for running a DNSD server
"""
@spec add_config(RawConfig.t(), map(), keyword()) :: RawConfig.t()
def add_config(
%RawConfig{
ifname: ifname,
files: files,
child_specs: child_specs
} = raw_config,
%{ipv4: %{method: :static, address: address}, dnsd: dnsd_config},
opts
) do
tmpdir = Keyword.fetch!(opts, :tmpdir)
dnsd = Keyword.fetch!(opts, :bin_dnsd)
dnsd_conf_path = Path.join(tmpdir, "dnsd.conf.#{ifname}")

new_files = [{dnsd_conf_path, dnsd_contents(dnsd_config)} | files]

dnsd_args =
[
"-c",
dnsd_conf_path,
"-i",
IP.ip_to_string(address)
]
|> add_port(dnsd_config)
|> add_ttl(dnsd_config)

new_child_specs =
child_specs ++
[
Supervisor.child_spec(
{MuonTrap.Daemon,
[
dnsd,
dnsd_args,
Command.add_muon_options(stderr_to_stdout: true, log_output: :debug)
]},
id: :dnsd
)
]

%RawConfig{raw_config | files: new_files, child_specs: new_child_specs}
end

def add_config(raw_config, _config_without_dhcpd, _opts), do: raw_config

defp dnsd_contents(%{records: records}) do
Enum.map(records, &record_to_string/1)
|> IO.iodata_to_binary()
end

defp record_to_string({name, ipa}) do
"#{name} #{IP.ip_to_string(ipa)}\n"
end

defp add_port(dnsd_args, %{port: port}) do
["-p", to_string(port) | dnsd_args]
end

defp add_port(dnsd_args, _dnsd_config), do: dnsd_args

defp add_ttl(dnsd_args, %{ttl: ttl}) do
["-t", to_string(ttl) | dnsd_args]
end

defp add_ttl(dnsd_args, _dnsd_config), do: dnsd_args
end
4 changes: 3 additions & 1 deletion lib/vintage_net/technology/wifi.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule VintageNet.Technology.WiFi do

alias VintageNet.WiFi.{WPA2, WPASupplicant}
alias VintageNet.Interface.RawConfig
alias VintageNet.IP.{IPv4Config, DhcpdConfig}
alias VintageNet.IP.{IPv4Config, DhcpdConfig, DnsdConfig}

# These configuration keys are common to all network specifications
# and allowed to pass through network normalization.
Expand Down Expand Up @@ -88,6 +88,7 @@ defmodule VintageNet.Technology.WiFi do
|> normalize_wifi()
|> IPv4Config.normalize()
|> DhcpdConfig.normalize()
|> DnsdConfig.normalize()
end

defp normalize_wifi(%{wifi: wifi} = config) do
Expand Down Expand Up @@ -270,6 +271,7 @@ defmodule VintageNet.Technology.WiFi do
}
|> IPv4Config.add_config(normalized_config, opts)
|> DhcpdConfig.add_config(normalized_config, opts)
|> DnsdConfig.add_config(normalized_config, opts)
end

@impl true
Expand Down
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ defmodule VintageNet.MixProject do
config: [],
tmpdir: "/tmp/vintage_net",
to_elixir_socket: "comms",
bin_dnsd: "dnsd",
bin_ifup: "ifup",
bin_ifdown: "ifdown",
bin_ifconfig: "ifconfig",
Expand Down
1 change: 1 addition & 0 deletions test/support/utils.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule VintageNetTest.Utils do
Application.get_all_env(:vintage_net)
|> Keyword.merge(
bin_chat: "chat",
bin_dnsd: "dnsd",
bin_ifdown: "ifdown",
bin_ifup: "ifup",
bin_ip: "ip",
Expand Down
81 changes: 81 additions & 0 deletions test/vintage_net/ip/dnsd_config_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
defmodule VintageNet.IP.DnsdConfigTest do
use ExUnit.Case

alias VintageNet.IP.DnsdConfig

test "dnsd normalizes" do
config = %{
ipv4: %{method: :static, address: {192, 168, 1, 1}, prefix_length: 24},
dnsd: %{
records: [
{"sample.com", "192.168.1.4"},
{"another.com", "192.168.1.5"}
]
}
}

normalized_config = %{
ipv4: %{method: :static, address: {192, 168, 1, 1}, prefix_length: 24},
dnsd: %{
records: [
{"sample.com", {192, 168, 1, 4}},
{"another.com", {192, 168, 1, 5}}
]
}
}

assert normalized_config == DnsdConfig.normalize(config)
end

test "Dnsd converts configs" do
input = %{
ipv4: %{method: :static, address: {192, 168, 1, 1}, prefix_length: 24},
dnsd: %{
records: [
{"sample.com", "192.168.1.4"},
{"another.com", "192.168.1.5"}
]
}
}

initial_raw_config = %VintageNet.Interface.RawConfig{
ifname: "eth0",
source_config: input,
type: UnitTest
}

opts = [tmpdir: "tmpdir", bin_dnsd: "dnsd"]

result = DnsdConfig.add_config(initial_raw_config, input, opts)

expected = %VintageNet.Interface.RawConfig{
child_specs: [
%{
id: :dnsd,
restart: :permanent,
shutdown: 500,
start:
{MuonTrap.Daemon, :start_link,
[
"dnsd",
["-c", "tmpdir/dnsd.conf.eth0", "-i", "192.168.1.1"],
[stderr_to_stdout: true, log_output: :debug]
]},
type: :worker
}
],
files: [
{"tmpdir/dnsd.conf.eth0",
"""
sample.com 192.168.1.4
another.com 192.168.1.5
"""}
],
ifname: "eth0",
source_config: input,
type: UnitTest
}

assert expected == result
end
end

0 comments on commit a1e89a4

Please sign in to comment.