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 = { { setuser = "nobody", keepNetAdmin = 1 }, { chroot = "/var/run/" }, { nofiles = 0 }, { noforks = 1 }, { seccomp = 0 }, { setupComplete = 1 } }, router = { supernodes = {}, 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", "supernodes", function(supernodes) table.insert(obj.router.supernodes, supernodes.public_key) end) for i,section in pairs(obj.security) do if type(section.seccomp) == "number" then obj.security[i].seccomp = tonumber(config.seccomp) end 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.security then for i,section in pairs(obj.security) do for key,value in pairs(section) do if key == "seccomp" then UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", { seccomp = tonumber(value) }) end end end 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