cjdns: import package from github.com:SeattleMeshnet/meshbox 89/head
authorDaniel Golle <daniel@makrotopia.org>
Fri, 17 Apr 2015 12:50:16 +0000 (14:50 +0200)
committerDaniel Golle <daniel@makrotopia.org>
Fri, 17 Apr 2015 12:50:16 +0000 (14:50 +0200)
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
cjdns/Makefile [new file with mode: 0644]
cjdns/files/cjdns.defaults [new file with mode: 0644]
cjdns/files/cjdns.init [new file with mode: 0755]
cjdns/files/cjdrouteconf [new file with mode: 0755]
cjdns/lua/cjdns/admin.lua [new file with mode: 0644]
cjdns/lua/cjdns/common.lua [new file with mode: 0644]
cjdns/lua/cjdns/init.lua [new file with mode: 0644]
cjdns/lua/cjdns/uci.lua [new file with mode: 0644]
cjdns/lua/cjdns/udp.lua [new file with mode: 0644]
cjdns/patches/010-fix-musl-build.patch [new file with mode: 0644]

diff --git a/cjdns/Makefile b/cjdns/Makefile
new file mode 100644 (file)
index 0000000..3253532
--- /dev/null
@@ -0,0 +1,100 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=cjdns
+PKG_VERSION:=0.16
+PKG_RELEASE:=9
+
+PKG_SOURCE_URL:=https://github.com/cjdelisle/cjdns.git
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_VERSION:=2138a1f6a94fc009958cde7b002c077a1eee929a
+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
+
+ifneq ($(CONFIG_KERNEL_SECCOMP_FILTER),y)
+PKG_DO_VARS:=Seccomp_NO=1
+endif
+
+define Build/Compile
+       CROSS="true" \
+       CC="$(TARGET_CC)" \
+       CFLAGS="$(TARGET_CFLAGS)" \
+       LDFLAGS="$(TARGET_LDFLAGS)" \
+       SYSTEM="linux" \
+       TARGET_ARCH="$(CONFIG_ARCH)" \
+       UCLIBC=1 \
+       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/cjdns/files/cjdns.defaults b/cjdns/files/cjdns.defaults
new file mode 100644 (file)
index 0000000..fd48269
--- /dev/null
@@ -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 type=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/cjdns/files/cjdns.init b/cjdns/files/cjdns.init
new file mode 100755 (executable)
index 0000000..b6371d7
--- /dev/null
@@ -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/cjdns/files/cjdrouteconf b/cjdns/files/cjdrouteconf
new file mode 100755 (executable)
index 0000000..fa5e073
--- /dev/null
@@ -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/cjdns/lua/cjdns/admin.lua b/cjdns/lua/cjdns/admin.lua
new file mode 100644 (file)
index 0000000..2bb58d2
--- /dev/null
@@ -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/cjdns/lua/cjdns/common.lua b/cjdns/lua/cjdns/common.lua
new file mode 100644 (file)
index 0000000..45f7dad
--- /dev/null
@@ -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/cjdns/lua/cjdns/init.lua b/cjdns/lua/cjdns/init.lua
new file mode 100644 (file)
index 0000000..32abbfc
--- /dev/null
@@ -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/cjdns/lua/cjdns/uci.lua b/cjdns/lua/cjdns/uci.lua
new file mode 100644 (file)
index 0000000..70c33bc
--- /dev/null
@@ -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/cjdns/lua/cjdns/udp.lua b/cjdns/lua/cjdns/udp.lua
new file mode 100644 (file)
index 0000000..9dd5901
--- /dev/null
@@ -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
diff --git a/cjdns/patches/010-fix-musl-build.patch b/cjdns/patches/010-fix-musl-build.patch
new file mode 100644 (file)
index 0000000..aa5648d
--- /dev/null
@@ -0,0 +1,29 @@
+Index: cjdns-649e26c7d61ccc66c20e87e1e3d381f9ef0cfcb0/crypto/random/seed/LinuxRandomUuidSysctlRandomSeed.c
+===================================================================
+--- cjdns-649e26c7d61ccc66c20e87e1e3d381f9ef0cfcb0.orig/crypto/random/seed/LinuxRandomUuidSysctlRandomSeed.c
++++ cjdns-649e26c7d61ccc66c20e87e1e3d381f9ef0cfcb0/crypto/random/seed/LinuxRandomUuidSysctlRandomSeed.c
+@@ -18,6 +18,8 @@
+ #include "util/Hex.h"
+ #include <unistd.h>
++
++#ifdef __GLIBC__
+ #include <sys/sysctl.h>
+ static int getUUID(uint64_t output[2])
+@@ -42,6 +44,15 @@ static int get(struct RandomSeed* random
+     return 0;
+ }
++#else
++
++static int get(struct RandomSeed* randomSeed, uint64_t output[8])
++{
++    return -1;
++}
++
++#endif
++
+ struct RandomSeed* LinuxRandomUuidSysctlRandomSeed_new(struct Allocator* alloc)
+ {
+     return Allocator_clone(alloc, (&(struct RandomSeed) {