--- /dev/null
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=cjdns
+PKG_VERSION:=0.16
+PKG_RELEASE:=10
+
+PKG_SOURCE_URL:=https://github.com/hyperboria/cjdns.git
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_VERSION:=0bf4e4b9cd364c4b806e1e382c111ec3cce3d640
+PKG_LICENSE:=GPL-3.0
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_SOURCE_VERSION).tar.bz2
+PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_SOURCE_VERSION)
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_SOURCE_VERSION)
+
+include $(INCLUDE_DIR)/package.mk
+
+
+define Package/cjdns
+ SECTION:=net
+ CATEGORY:=Network
+ SUBMENU:=Routing and Redirection
+ TITLE:=Encrypted near-zero-conf mesh routing protocol
+ URL:=https://github.com/hyperboria/cjdns
+ MAINTAINER:=Lars Gierth <larsg@systemli.org>
+ DEPENDS:=+kmod-tun +kmod-ipv6 +libnl-tiny +libpthread +librt \
+ +libuci-lua +lua-bencode +dkjson +luasocket +lua-sha2
+endef
+
+define Package/cjdns/description
+ Cjdns implements an encrypted IPv6 network using public-key cryptography \
+ for address allocation and a distributed hash table for routing. \
+ This provides near-zero-configuration networking, and prevents many \
+ of the security and scalability issues that plague existing networks.
+endef
+
+define Build/Configure
+endef
+
+PKG_DO_VARS:=
+
+ifneq ($(CONFIG_KERNEL_SECCOMP_FILTER),y)
+PKG_DO_VARS+= Seccomp_NO=1
+endif
+
+ifneq ($(CONFIG_USE_UCLIBC),)
+PKG_DO_VARS+= UCLIBC=1
+endif
+
+define Build/Compile
+ CROSS="true" \
+ CC="$(TARGET_CC)" \
+ CFLAGS="$(TARGET_CFLAGS)" \
+ LDFLAGS="$(TARGET_LDFLAGS)" \
+ SYSTEM="linux" \
+ TARGET_ARCH="$(CONFIG_ARCH)" \
+ SSP_SUPPORT="$(CONFIG_SSP_SUPPORT)" \
+ $(PKG_DO_VARS) \
+ $(PKG_BUILD_DIR)/do
+endef
+
+define Package/cjdns/install
+ $(INSTALL_DIR) \
+ $(1)/usr/sbin \
+ $(1)/usr/bin \
+ $(1)/etc/config \
+ $(1)/etc/init.d \
+ $(1)/etc/uci-defaults \
+ $(1)/usr/lib/lua/cjdns
+
+ $(INSTALL_BIN) \
+ ./files/cjdrouteconf \
+ $(1)/usr/bin
+
+ $(INSTALL_BIN) \
+ $(PKG_BUILD_DIR)/cjdroute \
+ $(1)/usr/sbin
+
+ $(INSTALL_BIN) \
+ $(PKG_BUILD_DIR)/publictoip6 \
+ $(1)/usr/bin
+
+ $(INSTALL_BIN) \
+ ./files/cjdns.init \
+ $(1)/etc/init.d/cjdns
+
+ $(INSTALL_BIN) \
+ ./files/cjdns.defaults \
+ $(1)/etc/uci-defaults/cjdns
+
+ $(CP) \
+ ./lua/cjdns/* \
+ $(1)/usr/lib/lua/cjdns
+endef
+
+define Package/cjdns/postinst
+#!/bin/sh
+if [ -z $${IPKG_INSTROOT} ] ; then
+ ( . /etc/uci-defaults/cjdns ) && rm -f /etc/uci-defaults/cjdns
+ # TODO: we should have an 'Enable' button instead
+ /etc/init.d/cjdns enabled || /etc/init.d/cjdns enable
+ exit 0
+fi
+endef
+
+$(eval $(call BuildPackage,cjdns))
--- /dev/null
+#!/bin/sh
+
+# if there is an existing config, our work is already done
+uci get cjdns.cjdns.ipv6 >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+
+ # register commit handler
+ uci -q batch <<-EOF >/dev/null
+ delete ucitrack.@cjdns[-1]
+ add ucitrack cjdns
+ set ucitrack.@cjdns[-1].init=cjdns
+ commit ucitrack
+EOF
+
+ # generate configuration
+ touch /etc/config/cjdns
+ cjdroute --genconf | cjdroute --cleanconf | cjdrouteconf set
+
+ # make sure config is present (might fail for any reason)
+ uci get cjdns.cjdns.ipv6 >/dev/null 2>&1
+ if [ $? -ne 0]; then
+ exit 1
+ fi
+
+ # enable auto-peering on ethernet
+ uci show network.lan | grep bridge >/dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ # most routers will set up an ethernet bridge for the lan
+ ifname="br-lan"
+ else
+ # docker containers don't have permission to create bridges by default,
+ # so we bind to the underlying interface instead (likely eth0)
+ ifname=`uci get network.lan.ifname`
+ fi
+ uci -q batch <<-EOF >/dev/null
+ add cjdns eth_interface
+ set cjdns.@eth_interface[-1].beacon=2
+ set cjdns.@eth_interface[-1].bind=$ifname
+EOF
+
+ # set the tun interface name
+ uci set cjdns.cjdns.tun_device=tuncjdns
+
+ # create the network interface
+ uci -q batch <<-EOF >/dev/null
+ set network.cjdns=interface
+ set network.cjdns.ifname=tuncjdns
+ set network.cjdns.proto=none
+EOF
+
+ # firewall rules by @dangowrt -- thanks <3
+
+ # create the firewall zone
+ uci -q batch <<-EOF >/dev/null
+ add firewall zone
+ set firewall.@zone[-1].name=cjdns
+ add_list firewall.@zone[-1].network=cjdns
+ set firewall.@zone[-1].input=REJECT
+ set firewall.@zone[-1].output=ACCEPT
+ set firewall.@zone[-1].forward=REJECT
+ set firewall.@zone[-1].conntrack=1
+ set firewall.@zone[-1].family=ipv6
+EOF
+
+ # allow ICMP from cjdns zone, e.g. ping6
+ uci -q batch <<-EOF >/dev/null
+ add firewall rule
+ set firewall.@rule[-1].name='Allow-ICMPv6-cjdns'
+ set firewall.@rule[-1].src=cjdns
+ set firewall.@rule[-1].proto=icmp
+ add_list firewall.@rule[-1].icmp_type=echo-request
+ add_list firewall.@rule[-1].icmp_type=echo-reply
+ add_list firewall.@rule[-1].icmp_type=destination-unreachable
+ add_list firewall.@rule[-1].icmp_type=packet-too-big
+ add_list firewall.@rule[-1].icmp_type=time-exceeded
+ add_list firewall.@rule[-1].icmp_type=bad-header
+ add_list firewall.@rule[-1].icmp_type=unknown-header-type
+ set firewall.@rule[-1].limit='1000/sec'
+ set firewall.@rule[-1].family=ipv6
+ set firewall.@rule[-1].target=ACCEPT
+EOF
+
+ # allow SSH from cjdns zone, needs to be explicitly enabled
+ uci -q batch <<-EOF >/dev/null
+ add firewall rule
+ set firewall.@rule[-1].enabled=0
+ set firewall.@rule[-1].name='Allow-SSH-cjdns'
+ set firewall.@rule[-1].src=cjdns
+ set firewall.@rule[-1].proto=tcp
+ set firewall.@rule[-1].dest_port=22
+ set firewall.@rule[-1].target=ACCEPT
+EOF
+
+ # allow LuCI access from cjdns zone, needs to be explicitly enabled
+ uci -q batch <<-EOF >/dev/null
+ add firewall rule
+ set firewall.@rule[-1].enabled=0
+ set firewall.@rule[-1].name='Allow-HTTP-cjdns'
+ set firewall.@rule[-1].src=cjdns
+ set firewall.@rule[-1].proto=tcp
+ set firewall.@rule[-1].dest_port=80
+ set firewall.@rule[-1].target=ACCEPT
+EOF
+
+ # allow UDP peering from wan zone, if it exists
+ uci show network.wan >/dev/null 2>&1
+ if [ $? -eq 0 ]; then
+ peeringPort=`uci get cjdns.@udp_interface[0].port`
+ uci -q batch <<-EOF >/dev/null
+ add firewall rule
+ set firewall.@rule[-1].name='Allow-cjdns-wan'
+ set firewall.@rule[-1].src=wan
+ set firewall.@rule[-1].proto=udp
+ set firewall.@rule[-1].dest_port=$peeringPort
+ set firewall.@rule[-1].target=ACCEPT
+EOF
+ fi
+
+ uci commit cjdns
+ uci commit firewall
+ uci commit network
+
+fi
+
+exit 0
--- /dev/null
+#!/bin/sh /etc/rc.common
+
+START=90
+STOP=85
+
+USE_PROCD=1
+
+start_service()
+{
+ [ -f /etc/uci-defaults/cjdns ] && ( . /etc/uci-defaults/cjdns )
+
+ procd_open_instance
+ procd_set_param respawn
+ procd_set_param command /bin/ash -c "cjdrouteconf get | tee /tmp/etc/cjdroute.conf | cjdroute --nobg | logger -t cjdns"
+ procd_close_instance
+}
+
+stop_service()
+{
+ killall cjdroute
+}
+
+reload_service()
+{
+ # cat /tmp/etc/cjdroute.conf | cjdrouteconf reload
+ restart
+}
+
+service_triggers()
+{
+ procd_add_reload_trigger cjdns
+}
--- /dev/null
+#!/usr/bin/env lua
+
+dkjson = require("dkjson")
+cjdns = require("cjdns")
+require("cjdns/uci")
+
+function help()
+ print("JSON interface to /etc/config/cjdns\n\nExamples: \
+ cjdrouteconf get > /tmp/etc/cjdroute.conf \
+ cat /tmp/etc/cjdroute.conf | cjdrouteconf set \
+ uci changes \
+ cjdrouteconf get | cjdroute")
+end
+
+if arg[1] == "get" then
+ local json = dkjson.encode(cjdns.uci.get(), { indent = true })
+ print(json)
+elseif arg[1] == "set" then
+ local json = io.stdin:read("*a")
+ local obj, pos, err = dkjson.decode(json, 1, nil)
+
+ if obj then
+ cjdns.uci.set(obj)
+ else
+ print("dkjson: " .. err .. " (try cjdroute --cleanconf)")
+ os.exit(1)
+ end
+else
+ help()
+end
--- /dev/null
+-- Cjdns admin module for Lua
+-- Written by Philip Horger
+
+common = require 'cjdns/common'
+
+AdminInterface = {}
+AdminInterface.__index = AdminInterface
+common.AdminInterface = AdminInterface
+
+function AdminInterface.new(properties)
+ properties = properties or {}
+
+ properties.host = properties.host or "127.0.0.1"
+ properties.port = properties.port or 11234
+ properties.password = properties.password or nil
+ properties.config = properties.config or common.ConfigFile.new("/etc/cjdroute.conf", false)
+ properties.timeout = properties.timeout or 2
+
+ properties.udp = common.UDPInterface.new(properties)
+
+ return setmetatable(properties, AdminInterface)
+end
+
+function AdminInterface:send(object)
+ local bencoded, err = bencode.encode(object)
+ if err then
+ return nil, err
+ end
+
+ local sock_obj = assert(socket.udp())
+ sock_obj:settimeout(self.timeout)
+
+ local _, err = sock_obj:sendto(bencoded, self.host, self.port)
+ if err then
+ return nil, err
+ end
+
+ return sock_obj
+end
+
+function AdminInterface:recv(sock_obj)
+ local retrieved, err = sock_obj:receive()
+ if not retrieved then
+ return nil, "ai:recv > " .. err
+ end
+ local bencoded, err = bencode.decode(retrieved)
+ if bencoded then
+ return bencoded
+ else
+ return nil, "ai:recv > " .. err
+ end
+end
+
+function AdminInterface:call(request)
+ local sock_obj, err = self:send(request)
+ if err then
+ return nil, "ai:call > " .. err
+ end
+
+ return self:recv(sock_obj)
+end
+
+function AdminInterface:getCookie()
+ local cookie_response, err = self:call({ q = "cookie" })
+ if not cookie_response then
+ return nil, "ai:getCookie > " .. err
+ end
+ return cookie_response.cookie
+end
+
+function AdminInterface:auth(request)
+ local funcname = request.q
+ local args = {}
+ for k, v in pairs(request) do
+ args[k] = v
+ end
+
+ -- Step 1: Get cookie
+ local cookie, err = self:getCookie()
+ if err then
+ return nil, err
+ end
+
+ -- Step 2: Calculate hash1 (password + cookie)
+ local plaintext1 = self.password .. cookie
+ local hash1 = sha2.sha256hex(plaintext1)
+
+ -- Step 3: Calculate hash2 (intermediate stage request)
+ local request = {
+ q = "auth",
+ aq = funcname,
+ args = args,
+ hash = hash1,
+ cookie = cookie
+ }
+ local plaintext2, err = bencode.encode(request)
+ if err then
+ return nil, err
+ end
+ local hash2 = sha2.sha256hex(plaintext2)
+
+ -- Step 4: Update hash in request, then ship it out
+ request.hash = hash2
+ return self:call(request)
+end
--- /dev/null
+-- Cjdns admin module for Lua
+-- Written by Philip Horger
+
+-- This table is preserved over multiple imports, and collects
+-- submodules import-by-import via init.lua.
+
+return {}
--- /dev/null
+-- Cjdns admin module for Lua
+-- Written by Philip Horger
+
+bencode = require "bencode" -- https://bitbucket.org/wilhelmy/lua-bencode/
+dkjson = require "dkjson" -- http://dkolf.de/src/dkjson-lua.fsl/home
+socket = require "socket" -- http://w3.impa.br/~diego/software/luasocket/
+sha2 = require "sha2" -- https://code.google.com/p/sha2/
+
+require "cjdns/admin"
+require "cjdns/udp"
+
+return require "cjdns/common"
--- /dev/null
+common = require("cjdns/common")
+uci = require("uci")
+
+UCI = {}
+common.uci = UCI
+
+--- Return the configuration defaults as a table suitable for JSON output
+--
+-- Mostly taken from cjdroute --genconf
+-- @return table with configuration defaults
+function UCI.defaults()
+ return {
+ security = { { exemptAngel = 1, setuser = "nobody" } },
+ router = {
+ ipTunnel = { outgoingConnections = {}, allowedConnections = {} },
+ interface = { type = "TUNInterface" }
+ },
+ interfaces = { UDPInterface = {}, ETHInterface = {} },
+ authorizedPasswords = {},
+ logging = { logTo = "stdout" }
+ }
+end
+
+--- Return the cjdns configuration as a table suitable for JSON output
+--
+-- Iterates over cjdns, eth_interface, udp_interface, eth_peer, udp_peer,
+-- and password sections. Doesn't include IPTunnel related options yet.
+-- @return table with cjdns configuration
+function UCI.get()
+ local obj = UCI.defaults()
+
+ local cursor = uci.cursor()
+
+ local config = cursor:get_all("cjdns", "cjdns")
+ if not config then return obj end
+
+ obj.ipv6 = config.ipv6
+ obj.publicKey = config.public_key
+ obj.privateKey = config.private_key
+ obj.admin = {
+ bind = config.admin_address .. ":" .. config.admin_port,
+ password = config.admin_password }
+
+ if config.tun_device and string.len(config.tun_device) > 0 then
+ obj.router.interface.tunDevice = config.tun_device
+ end
+
+ cursor:foreach("cjdns", "iptunnel_outgoing", function(outgoing)
+ table.insert(obj.router.ipTunnel.outgoingConnections, outgoing.public_key)
+ end)
+
+ cursor:foreach("cjdns", "iptunnel_allowed", function(allowed)
+ entry = { publicKey = allowed.public_key }
+ if allowed.ipv4 then
+ entry["ip4Address"] = allowed.ipv4
+ end
+ if allowed.ipv6 then
+ entry["ip6Address"] = allowed.ipv6
+ end
+ table.insert(obj.router.ipTunnel.allowedConnections, entry)
+ end)
+
+ cursor:foreach("cjdns", "eth_interface", function(eth_interface)
+ table.insert(obj.interfaces.ETHInterface, {
+ bind = eth_interface.bind,
+ beacon = tonumber(eth_interface.beacon),
+ connectTo = {}
+ })
+ end)
+
+ cursor:foreach("cjdns", "udp_interface", function(udp_interface)
+ table.insert(obj.interfaces.UDPInterface, {
+ bind = udp_interface.address .. ":" .. udp_interface.port,
+ connectTo = {}
+ })
+ end)
+
+ cursor:foreach("cjdns", "eth_peer", function(eth_peer)
+ if not eth_peer.address == "" then
+ local i = tonumber(eth_peer.interface)
+ obj.interfaces.ETHInterface[i].connectTo[eth_peer.address] = {
+ publicKey = eth_peer.public_key,
+ password = eth_peer.password
+ }
+ end
+ end)
+
+ cursor:foreach("cjdns", "udp_peer", function(udp_peer)
+ local bind = udp_peer.address .. ":" .. udp_peer.port
+ local i = tonumber(udp_peer.interface)
+ obj.interfaces.UDPInterface[i].connectTo[bind] = {
+ user = udp_peer.user,
+ publicKey = udp_peer.public_key,
+ password = udp_peer.password
+ }
+ end)
+
+ cursor:foreach("cjdns", "password", function(password)
+ table.insert(obj.authorizedPasswords, {
+ password = password.password,
+ user = password.user,
+ contact = password.contact
+ })
+ end)
+
+ return obj
+end
+
+--- Parse and save updated configuration from JSON input
+--
+-- Transforms general settings, ETHInterface, UDPInterface, connectTo, and
+-- authorizedPasswords fields into UCI sections, and replaces the UCI config's
+-- contents with them.
+-- @param table JSON input
+-- @return Boolean whether saving succeeded
+function UCI.set(obj)
+ local cursor = uci.cursor()
+
+ for i, section in pairs(cursor:get_all("cjdns")) do
+ cursor:delete("cjdns", section[".name"])
+ end
+
+ local admin_address, admin_port = string.match(obj.admin.bind, "^(.*):(.*)$")
+ UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
+ ipv6 = obj.ipv6,
+ public_key = obj.publicKey,
+ private_key = obj.privateKey,
+ admin_password = obj.admin.password,
+ admin_address = admin_address,
+ admin_port = admin_port,
+ })
+
+ if obj.router.interface.tunDevice then
+ UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
+ tun_device = tostring(obj.router.interface.tunDevice)
+ })
+ end
+
+ if obj.router.ipTunnel.outgoingConnections then
+ for i,public_key in pairs(obj.router.ipTunnel.outgoingConnections) do
+ UCI.cursor_section(cursor, "cjdns", "iptunnel_outgoing", nil, {
+ public_key = public_key
+ })
+ end
+ end
+
+ if obj.router.ipTunnel.allowedConnections then
+ for i,allowed in pairs(obj.router.ipTunnel.allowedConnections) do
+ entry = { public_key = allowed.publicKey }
+ if allowed.ip4Address then
+ entry["ipv4"] = allowed.ip4Address
+ end
+ if allowed.ip6Address then
+ entry["ipv6"] = allowed.ip6Address
+ end
+
+ UCI.cursor_section(cursor, "cjdns", "iptunnel_allowed", nil, entry)
+ end
+ end
+
+ if obj.interfaces.ETHInterface then
+ for i,interface in pairs(obj.interfaces.ETHInterface) do
+ UCI.cursor_section(cursor, "cjdns", "eth_interface", nil, {
+ bind = interface.bind,
+ beacon = tostring(interface.beacon)
+ })
+
+ if interface.connectTo then
+ for peer_address,peer in pairs(interface.connectTo) do
+ UCI.cursor_section(cursor, "cjdns", "eth_peer", nil, {
+ interface = i,
+ address = peer_address,
+ public_key = peer.publicKey,
+ password = peer.password
+ })
+ end
+ end
+ end
+ end
+
+ if obj.interfaces.UDPInterface then
+ for i,interface in pairs(obj.interfaces.UDPInterface) do
+ local address, port = string.match(interface.bind, "^(.*):(.*)$")
+ UCI.cursor_section(cursor, "cjdns", "udp_interface", nil, {
+ address = address,
+ port = port
+ })
+
+ if interface.connectTo then
+ for peer_bind,peer in pairs(interface.connectTo) do
+ local peer_address, peer_port = string.match(peer_bind, "^(.*):(.*)$")
+ UCI.cursor_section(cursor, "cjdns", "udp_peer", nil, {
+ interface = i,
+ address = peer_address,
+ port = peer_port,
+ user = peer.user,
+ public_key = peer.publicKey,
+ password = peer.password
+ })
+ end
+ end
+ end
+ end
+
+ if obj.authorizedPasswords then
+ for i,password in pairs(obj.authorizedPasswords) do
+ local user = password.user
+ if not user or string.len(user) == 0 then
+ user = "user-" .. UCI.random_string(6)
+ end
+
+ UCI.cursor_section(cursor, "cjdns", "password", nil, {
+ password = password.password,
+ user = user,
+ contact = password.contact
+ })
+ end
+ end
+
+ return cursor:save("cjdns")
+end
+
+--- Simple backport of Cursor:section from luci.model.uci
+--
+-- Backport reason: we don't wanna depend on LuCI.
+-- @param Cursor the UCI cursor to operate on
+-- @param string name of the config
+-- @param string type of the section
+-- @param string name of the section (optional)
+-- @param table config values
+function UCI.cursor_section(cursor, config, type, section, values)
+ if section then
+ cursor:set(config, section, type)
+ else
+ section = cursor:add("cjdns", type)
+ end
+
+ for k,v in pairs(values) do
+ cursor:set(config, section, k, v)
+ end
+end
+
+function UCI.makeInterface()
+ local cursor = uci.cursor()
+
+ local config = cursor:get_all("cjdns", "cjdns")
+ if not config then return nil end
+
+ return common.AdminInterface.new({
+ host = config.admin_address,
+ port = config.admin_port,
+ password = config.admin_password,
+ config = UCI.get(),
+ timeout = 2
+ })
+end
+
+function UCI.random_string(length)
+ -- tr -cd 'A-Za-z0-9' < /dev/urandom
+ local urandom = io.popen("tr -cd 'A-Za-z0-9' 2> /dev/null < /dev/urandom", "r")
+ local string = urandom:read(length)
+ urandom:close()
+ return string
+end
--- /dev/null
+-- Cjdns admin module for Lua
+-- Written by Philip Horger
+
+common = require 'cjdns/common'
+
+UDPInterface = {}
+UDPInterface.__index = UDPInterface
+common.UDPInterface = UDPInterface
+
+function UDPInterface.new(ai, config, ptype)
+ properties = {
+ ai = ai,
+ config = config or ai.config,
+ ptype = ptype or "ai"
+ }
+
+ return setmetatable(properties, UDPInterface)
+end
+
+function UDPInterface:call(name, args)
+ local func = self[name .. "_" .. self.ptype]
+ return func(self, unpack(args))
+end
+
+function UDPInterface:newBind(...)
+ return self:call("newBind", arg)
+end
+
+function UDPInterface:beginConnection(...)
+ return self:call("beginConnection", arg)
+end
+
+function UDPInterface:newBind_ai(address)
+ local response, err = self.ai:auth({
+ q = "UDPInterface_new",
+ bindAddress = address
+ })
+ if not response then
+ return nil, err
+ elseif response.error ~= "none" then
+ return nil, response.error
+ elseif response.interfaceNumber then
+ return response.interfaceNumber
+ else
+ return nil, "bad response format"
+ end
+end
+
+function UDPInterface:newBind_config(address)
+ local udpif = self.config.contents.interfaces.UDPInterface
+ local new_interface = {
+ bind = address,
+ connectTo = {}
+ }
+ table.insert(udpif, new_interface)
+ return (#udpif - 1), new_interface
+end
+
+function UDPInterface:newBind_perm(...)
+ return
+ self:newBind_config(unpack(arg)),
+ self:newBind_ai(unpack(arg))
+end
+
+function UDPInterface:beginConnection_ai(pubkey, addr, password, interface)
+ local request = {
+ q = "UDPInterface_beginConnection",
+ publicKey = pubkey,
+ address = addr,
+ password = password
+ }
+ if interface then
+ request.interfaceNumber = interface
+ end
+
+ local response, err = self.ai:auth(request)
+ if not response then
+ return nil, err
+ elseif response.error == "none" then
+ -- Unfortunately, no real success indicator either.
+ return "No error"
+ else
+ return nil, response.error
+ end
+end
+
+function UDPInterface:beginConnection_config(pubkey, addr, password, interface)
+ local udpif = self.config.contents.interfaces.UDPInterface
+ local connections = udpif[(interface or 0) + 1].connectTo
+ local this_conn = {
+ password = password,
+ publicKey = pubkey
+ }
+ connections[addr] = this_conn
+ return this_conn -- allows adding metadata fields afterwards
+end
+
+function UDPInterface:beginConnection_perm(...)
+ return
+ self:beginConnection_config(unpack(arg)),
+ self:beginConnection_ai(unpack(arg))
+end