pull/1181/merge
Connor Rigby 2020-05-22 21:24:15 +00:00 committed by GitHub
commit 053754000c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 103 additions and 97 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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)

View File

@ -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},

View File

@ -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

View File

@ -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"
}

View File

@ -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