Remove usage of dnsmasq
This implementes a small DNS server that can be controlled/setup by VintageNet natively, instead of shelling out to dnsmasq. It also by extension removes the usage of dnsmasq's dhcp server, replacing it with dhcpcd which is built into VintageNet as well.remove-dnsmasq
parent
e6b0f53224
commit
5e483c01e2
|
@ -10,22 +10,7 @@ config :vintage_net,
|
|||
persistence: VintageNet.Persistence.Null,
|
||||
config: [
|
||||
{"wlan0", %{type: VintageNet.Technology.Null}},
|
||||
{"usb0",
|
||||
%{
|
||||
type: FarmbotOS.Platform.Target.Configurator.CaptivePortal,
|
||||
ipv4: %{
|
||||
method: :static,
|
||||
address: "192.168.25.1",
|
||||
netmask: "255.255.255.0"
|
||||
},
|
||||
dnsmasq: %{
|
||||
domain: "farmbot",
|
||||
server: "192.168.25.1",
|
||||
address: "192.168.25.1",
|
||||
start: "192.168.25.2",
|
||||
end: "192.168.25.10"
|
||||
}
|
||||
}}
|
||||
{"usb0", %{type: VintageNetDirect}}
|
||||
]
|
||||
|
||||
config :mdns_lite,
|
||||
|
|
|
@ -10,22 +10,7 @@ config :vintage_net,
|
|||
persistence: VintageNet.Persistence.Null,
|
||||
config: [
|
||||
{"wlan0", %{type: VintageNet.Technology.Null}},
|
||||
{"usb0",
|
||||
%{
|
||||
type: FarmbotOS.Platform.Target.Configurator.CaptivePortal,
|
||||
ipv4: %{
|
||||
method: :static,
|
||||
address: "192.168.25.1",
|
||||
netmask: "255.255.255.0"
|
||||
},
|
||||
dnsmasq: %{
|
||||
domain: "farmbot",
|
||||
server: "192.168.25.1",
|
||||
address: "192.168.25.1",
|
||||
start: "192.168.25.2",
|
||||
end: "192.168.25.10"
|
||||
}
|
||||
}}
|
||||
{"usb0", %{type: VintageNetDirect}}
|
||||
]
|
||||
|
||||
config :mdns_lite,
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
defmodule FarmbotOS.Configurator.CaptiveDNS do
|
||||
use GenServer
|
||||
alias __MODULE__, as: State
|
||||
|
||||
defstruct [:dns_socket, :dns_port, :ifname]
|
||||
|
||||
def start_link(ifname, port) do
|
||||
GenServer.start_link(__MODULE__, [ifname, port])
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
def init([ifname, port]) do
|
||||
send(self(), :open_dns)
|
||||
# use charlist here because :inet module works with charlists
|
||||
{:ok, %State{dns_port: port, ifname: to_charlist(ifname)}}
|
||||
end
|
||||
|
||||
@impl GenServer
|
||||
# open a UDP socket on port 53
|
||||
def handle_info(:open_dns, state) do
|
||||
case :gen_udp.open(state.dns_port, [:binary, active: true, reuseaddr: true]) do
|
||||
{:ok, socket} ->
|
||||
{:noreply, %State{state | dns_socket: socket}}
|
||||
|
||||
error ->
|
||||
{:stop, error, state}
|
||||
end
|
||||
end
|
||||
|
||||
# binary dns message from the socket
|
||||
def handle_info(
|
||||
{:udp, socket, ip, port, packet},
|
||||
%{dns_socket: socket} = state
|
||||
) do
|
||||
record = DNS.Record.decode(packet)
|
||||
{answers, state} = handle_dns(record.qdlist, [], state)
|
||||
response = DNS.Record.encode(%{record | anlist: answers})
|
||||
_ = :gen_udp.send(socket, ip, port, response)
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
# recursively check for dns queries, respond to each of them with the local ip address.
|
||||
|
||||
# respond to `a` with our current ip address
|
||||
defp handle_dns(
|
||||
[%{type: :a} = q | rest],
|
||||
answers,
|
||||
state
|
||||
) do
|
||||
ifname = state.ifname
|
||||
{:ok, interfaces} = :inet.getifaddrs()
|
||||
{^ifname, ifinfo} = List.keyfind(interfaces, ifname, 0)
|
||||
|
||||
addr =
|
||||
Enum.find_value(ifinfo, fn
|
||||
{:addr, {_, _, _, _} = ipv4_addr} -> ipv4_addr
|
||||
_ -> false
|
||||
end)
|
||||
|
||||
answer = make_record(q.domain, q.type, 120, addr)
|
||||
handle_dns(rest, [answer | answers], state)
|
||||
end
|
||||
|
||||
# stop recursing when qdlist is fully enumerated
|
||||
defp handle_dns([], answers, state) do
|
||||
{Enum.reverse(answers), state}
|
||||
end
|
||||
|
||||
defp make_record(domain, type, ttl, data) do
|
||||
%DNS.Resource{
|
||||
domain: domain,
|
||||
class: :in,
|
||||
type: type,
|
||||
ttl: ttl,
|
||||
data: data
|
||||
}
|
||||
end
|
||||
end
|
||||
FarmbotOS.Configurator.CaptiveDNS.start_link("lo0", 4040)
|
|
@ -92,6 +92,7 @@ defmodule FarmbotOS.MixProject do
|
|||
{:cors_plug, "~> 2.0"},
|
||||
{:plug_cowboy, "~> 2.1"},
|
||||
{:phoenix_html, "~> 2.13"},
|
||||
{:dns, "~> 2.1"},
|
||||
|
||||
# Nerves stuff.
|
||||
{:nerves, "~> 1.5", runtime: false},
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
|
|||
|
||||
@behaviour VintageNet.Technology
|
||||
require FarmbotCore.Logger
|
||||
alias FarmbotOS.Configurator.CaptiveDNS
|
||||
|
||||
@impl VintageNet.Technology
|
||||
def normalize(%{vintage_net_wifi: _} = config) do
|
||||
|
@ -24,7 +25,7 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
|
|||
|
||||
ifname
|
||||
|> vintage_wifi(normalized, opts)
|
||||
|> dnsmasq(opts)
|
||||
|> add_captive_dns()
|
||||
end
|
||||
|
||||
def to_raw_config(ifname, config, opts) do
|
||||
|
@ -32,7 +33,7 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
|
|||
|
||||
ifname
|
||||
|> vintage_ethernet(normalized, opts)
|
||||
|> dnsmasq(opts)
|
||||
|> add_captive_dns()
|
||||
end
|
||||
|
||||
@impl VintageNet.Technology
|
||||
|
@ -45,67 +46,12 @@ defmodule FarmbotOS.Platform.Target.Configurator.CaptivePortal do
|
|||
VintageNetWiFi.ioctl(ifname, ioctl, args)
|
||||
end
|
||||
|
||||
defp dnsmasq(
|
||||
%{ifname: ifname, source_config: %{dnsmasq: config}} = raw_config,
|
||||
opts
|
||||
) do
|
||||
tmpdir = Keyword.fetch!(opts, :tmpdir)
|
||||
killall = Keyword.fetch!(opts, :bin_killall)
|
||||
dnsmasq = System.find_executable("dnsmasq")
|
||||
dnsmasq_conf_path = Path.join(tmpdir, "dnsmasq.conf.#{ifname}")
|
||||
dnsmasq_lease_file = Path.join(tmpdir, "dnsmasq.leases.#{ifname}")
|
||||
dnsmasq_pid_file = Path.join(tmpdir, "dnsmasq.pid.#{ifname}")
|
||||
|
||||
dnsmasq_conf_contents = """
|
||||
interface=#{ifname}
|
||||
except-interface=lo
|
||||
localise-queries
|
||||
bogus-priv
|
||||
bind-interfaces
|
||||
listen-address=#{config[:address]}
|
||||
server=#{config[:address]}
|
||||
address=/#/#{config[:address]}
|
||||
dhcp-option=6,#{config[:address]}
|
||||
dhcp-range=#{config[:start]},#{config[:end]},12h
|
||||
"""
|
||||
|
||||
files = [
|
||||
{dnsmasq_conf_path, dnsmasq_conf_contents}
|
||||
defp add_captive_dns(%{ifname: ifname} = raw_config) do
|
||||
child_specs = [
|
||||
{CaptiveDNS, [ifname, 53]}
|
||||
]
|
||||
|
||||
up_cmds = [
|
||||
{:run, dnsmasq,
|
||||
[
|
||||
"-K",
|
||||
"-l",
|
||||
dnsmasq_lease_file,
|
||||
"-x",
|
||||
dnsmasq_pid_file,
|
||||
"-C",
|
||||
dnsmasq_conf_path
|
||||
]}
|
||||
]
|
||||
|
||||
down_cmds = [
|
||||
{:run, killall, ["-q", "-9", "dnsmasq"]}
|
||||
]
|
||||
|
||||
updated_raw_config = %{
|
||||
raw_config
|
||||
| files: raw_config.files ++ files,
|
||||
up_cmds: raw_config.up_cmds ++ up_cmds,
|
||||
down_cmds: raw_config.down_cmds ++ down_cmds,
|
||||
cleanup_files:
|
||||
raw_config.cleanup_files ++
|
||||
[dnsmasq_conf_path, dnsmasq_lease_file, dnsmasq_pid_file]
|
||||
}
|
||||
|
||||
updated_raw_config
|
||||
end
|
||||
|
||||
defp dnsmasq(%{} = raw_config, _opts) do
|
||||
FarmbotCore.Logger.error(1, "DNSMASQ Disabled")
|
||||
raw_config
|
||||
%{raw_config | child_specs: raw_config.child_specs ++ child_specs}
|
||||
end
|
||||
|
||||
defp vintage_wifi(ifname, config, opts) do
|
||||
|
|
|
@ -36,10 +36,7 @@ defmodule FarmbotOS.Platform.Target.Network do
|
|||
address: "192.168.24.1",
|
||||
netmask: "255.255.255.0"
|
||||
},
|
||||
dnsmasq: %{
|
||||
domain: "farmbot",
|
||||
server: "192.168.24.1",
|
||||
address: "192.168.24.1",
|
||||
dhcpd: %{
|
||||
start: "192.168.24.2",
|
||||
end: "192.168.24.10"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
defmodule FarmbotOS.Configurator.CaptiveDNSTest do
|
||||
use ExUnit.Case, async: false
|
||||
|
||||
@dig System.find_executable("dig")
|
||||
if @dig do
|
||||
test "all dns queries resolve to local ip address" do
|
||||
{:ok, dns_server} = FarmbotOS.Configurator.CaptiveDNS.start_link("lo0", 4040)
|
||||
res = :os.cmd('dig -p 4040')
|
||||
refute String.contains?(to_string(res), "no servers could be reached")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue