From: nynex Date: Thu, 30 Apr 2015 17:19:29 +0000 (+0000) Subject: updated cjdns version X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=daf7c22c61556d080ca09ecb6dbb848f4be609c2;p=librecmc%2Flibrecmc-fossil.git updated cjdns version --- diff --git a/trunk/package/network/services/cjdns/Makefile b/trunk/package/network/services/cjdns/Makefile new file mode 100644 index 00000000..b9c2a5fb --- /dev/null +++ b/trunk/package/network/services/cjdns/Makefile @@ -0,0 +1,105 @@ +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 + 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)) diff --git a/trunk/package/network/services/cjdns/files/cjdns.defaults b/trunk/package/network/services/cjdns/files/cjdns.defaults new file mode 100644 index 00000000..f6ca7720 --- /dev/null +++ b/trunk/package/network/services/cjdns/files/cjdns.defaults @@ -0,0 +1,125 @@ +#!/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 diff --git a/trunk/package/network/services/cjdns/files/cjdns.init b/trunk/package/network/services/cjdns/files/cjdns.init new file mode 100755 index 00000000..b6371d78 --- /dev/null +++ b/trunk/package/network/services/cjdns/files/cjdns.init @@ -0,0 +1,32 @@ +#!/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 +} diff --git a/trunk/package/network/services/cjdns/files/cjdrouteconf b/trunk/package/network/services/cjdns/files/cjdrouteconf new file mode 100755 index 00000000..fa5e0735 --- /dev/null +++ b/trunk/package/network/services/cjdns/files/cjdrouteconf @@ -0,0 +1,30 @@ +#!/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 diff --git a/trunk/package/network/services/cjdns/lua/cjdns/admin.lua b/trunk/package/network/services/cjdns/lua/cjdns/admin.lua new file mode 100644 index 00000000..2bb58d29 --- /dev/null +++ b/trunk/package/network/services/cjdns/lua/cjdns/admin.lua @@ -0,0 +1,105 @@ +-- 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 diff --git a/trunk/package/network/services/cjdns/lua/cjdns/common.lua b/trunk/package/network/services/cjdns/lua/cjdns/common.lua new file mode 100644 index 00000000..45f7dad9 --- /dev/null +++ b/trunk/package/network/services/cjdns/lua/cjdns/common.lua @@ -0,0 +1,7 @@ +-- 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 {} diff --git a/trunk/package/network/services/cjdns/lua/cjdns/init.lua b/trunk/package/network/services/cjdns/lua/cjdns/init.lua new file mode 100644 index 00000000..32abbfc8 --- /dev/null +++ b/trunk/package/network/services/cjdns/lua/cjdns/init.lua @@ -0,0 +1,12 @@ +-- 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" diff --git a/trunk/package/network/services/cjdns/lua/cjdns/uci.lua b/trunk/package/network/services/cjdns/lua/cjdns/uci.lua new file mode 100644 index 00000000..70c33bc1 --- /dev/null +++ b/trunk/package/network/services/cjdns/lua/cjdns/uci.lua @@ -0,0 +1,264 @@ +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 diff --git a/trunk/package/network/services/cjdns/lua/cjdns/udp.lua b/trunk/package/network/services/cjdns/lua/cjdns/udp.lua new file mode 100644 index 00000000..9dd59019 --- /dev/null +++ b/trunk/package/network/services/cjdns/lua/cjdns/udp.lua @@ -0,0 +1,102 @@ +-- 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