Add support for miniupnpd and cjdns
[librecmc/package-feed.git] / net / cjdns / lua / cjdns / uci.lua
1 common = require("cjdns/common")
2 uci    = require("uci")
3
4 UCI = {}
5 common.uci = UCI
6
7 --- Return the configuration defaults as a table suitable for JSON output
8 --
9 -- Mostly taken from cjdroute --genconf
10 -- @return table with configuration defaults
11 function UCI.defaults()
12   return {
13     security = {
14       { setuser = "nobody", keepNetAdmin = 1 },
15       { chroot = "/var/run/" },
16       { nofiles = 0 },
17       { noforks = 1 },
18       { seccomp = 0 },
19       { setupComplete = 1 }
20     },
21     router = {
22         ipTunnel = { outgoingConnections = {}, allowedConnections = {} },
23         interface = { type = "TUNInterface" }
24     },
25     interfaces = { UDPInterface = {}, ETHInterface = {} },
26     authorizedPasswords = {},
27     logging = { logTo = "stdout" }
28   }
29 end
30
31 --- Return the cjdns configuration as a table suitable for JSON output
32 --
33 -- Iterates over cjdns, eth_interface, udp_interface, eth_peer, udp_peer,
34 -- and password sections. Doesn't include IPTunnel related options yet.
35 -- @return table with cjdns configuration
36 function UCI.get()
37   local obj = UCI.defaults()
38
39   local cursor = uci.cursor()
40
41   local config = cursor:get_all("cjdns", "cjdns")
42   if not config then return obj end
43
44   obj.ipv6 = config.ipv6
45   obj.publicKey = config.public_key
46   obj.privateKey = config.private_key
47   obj.admin = {
48     bind = config.admin_address .. ":" .. config.admin_port,
49     password = config.admin_password }
50
51   if config.tun_device and string.len(config.tun_device) > 0 then
52     obj.router.interface.tunDevice = config.tun_device
53   end
54
55   for i,section in pairs(obj.security) do
56     if type(section.seccomp) == "number" then
57       obj.security[i].seccomp = tonumber(config.seccomp)
58     end
59   end
60
61   cursor:foreach("cjdns", "iptunnel_outgoing", function(outgoing)
62     table.insert(obj.router.ipTunnel.outgoingConnections, outgoing.public_key)
63   end)
64
65   cursor:foreach("cjdns", "iptunnel_allowed", function(allowed)
66     entry = { publicKey = allowed.public_key }
67     if allowed.ipv4 then
68       entry["ip4Address"] = allowed.ipv4
69     end
70     if allowed.ipv6 then
71       entry["ip6Address"] = allowed.ipv6
72     end
73     table.insert(obj.router.ipTunnel.allowedConnections, entry)
74   end)
75
76   cursor:foreach("cjdns", "eth_interface", function(eth_interface)
77     table.insert(obj.interfaces.ETHInterface, {
78       bind = eth_interface.bind,
79       beacon = tonumber(eth_interface.beacon),
80       connectTo = {}
81     })
82   end)
83
84   cursor:foreach("cjdns", "udp_interface", function(udp_interface)
85     table.insert(obj.interfaces.UDPInterface, {
86       bind = udp_interface.address .. ":" .. udp_interface.port,
87       connectTo = {}
88     })
89   end)
90
91   cursor:foreach("cjdns", "eth_peer", function(eth_peer)
92     if not eth_peer.address == "" then
93       local i = tonumber(eth_peer.interface)
94       obj.interfaces.ETHInterface[i].connectTo[eth_peer.address] = {
95         publicKey = eth_peer.public_key,
96         password = eth_peer.password
97       }
98     end
99   end)
100
101   cursor:foreach("cjdns", "udp_peer", function(udp_peer)
102     local bind = udp_peer.address .. ":" .. udp_peer.port
103     local i = tonumber(udp_peer.interface)
104     obj.interfaces.UDPInterface[i].connectTo[bind] = {
105       user = udp_peer.user,
106       publicKey = udp_peer.public_key,
107       password = udp_peer.password
108     }
109   end)
110
111   cursor:foreach("cjdns", "password", function(password)
112     table.insert(obj.authorizedPasswords, {
113       password = password.password,
114       user = password.user,
115       contact = password.contact
116     })
117   end)
118
119   return obj
120 end
121
122 --- Parse and save updated configuration from JSON input
123 --
124 -- Transforms general settings, ETHInterface, UDPInterface, connectTo, and
125 -- authorizedPasswords fields into UCI sections, and replaces the UCI config's
126 -- contents with them.
127 -- @param table JSON input
128 -- @return Boolean whether saving succeeded
129 function UCI.set(obj)
130   local cursor = uci.cursor()
131
132   for i, section in pairs(cursor:get_all("cjdns")) do
133     cursor:delete("cjdns", section[".name"])
134   end
135
136   local admin_address, admin_port = string.match(obj.admin.bind, "^(.*):(.*)$")
137   UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
138     ipv6 = obj.ipv6,
139     public_key = obj.publicKey,
140     private_key = obj.privateKey,
141     admin_password = obj.admin.password,
142     admin_address = admin_address,
143     admin_port = admin_port
144   })
145
146   if obj.router.interface.tunDevice then
147     UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
148       tun_device = tostring(obj.router.interface.tunDevice)
149     })
150   end
151
152   if obj.security then
153     for i,section in pairs(obj.security) do
154       for key,value in pairs(section) do
155         if key == "seccomp" then
156           UCI.cursor_section(cursor, "cjdns", "cjdns", "cjdns", {
157             seccomp = tonumber(value)
158           })
159         end
160       end
161     end
162   end
163
164   if obj.router.ipTunnel.outgoingConnections then
165     for i,public_key in pairs(obj.router.ipTunnel.outgoingConnections) do
166       UCI.cursor_section(cursor, "cjdns", "iptunnel_outgoing", nil, {
167         public_key = public_key
168       })
169     end
170   end
171
172   if obj.router.ipTunnel.allowedConnections then
173     for i,allowed in pairs(obj.router.ipTunnel.allowedConnections) do
174       entry = { public_key = allowed.publicKey }
175       if allowed.ip4Address then
176         entry["ipv4"] = allowed.ip4Address
177       end
178       if allowed.ip6Address then
179         entry["ipv6"] = allowed.ip6Address
180       end
181
182       UCI.cursor_section(cursor, "cjdns", "iptunnel_allowed", nil, entry)
183     end
184   end
185
186   if obj.interfaces.ETHInterface then
187     for i,interface in pairs(obj.interfaces.ETHInterface) do
188       UCI.cursor_section(cursor, "cjdns", "eth_interface", nil, {
189         bind = interface.bind,
190         beacon = tostring(interface.beacon)
191       })
192
193       if interface.connectTo then
194         for peer_address,peer in pairs(interface.connectTo) do
195           UCI.cursor_section(cursor, "cjdns", "eth_peer", nil, {
196             interface = i,
197             address = peer_address,
198             public_key = peer.publicKey,
199             password = peer.password
200           })
201         end
202       end
203     end
204   end
205
206   if obj.interfaces.UDPInterface then
207     for i,interface in pairs(obj.interfaces.UDPInterface) do
208       local address, port = string.match(interface.bind, "^(.*):(.*)$")
209       UCI.cursor_section(cursor, "cjdns", "udp_interface", nil, {
210         address = address,
211         port = port
212       })
213
214       if interface.connectTo then
215         for peer_bind,peer in pairs(interface.connectTo) do
216           local peer_address, peer_port = string.match(peer_bind, "^(.*):(.*)$")
217           UCI.cursor_section(cursor, "cjdns", "udp_peer", nil, {
218             interface = i,
219             address = peer_address,
220             port = peer_port,
221             user = peer.user,
222             public_key = peer.publicKey,
223             password = peer.password
224           })
225         end
226       end
227     end
228   end
229
230   if obj.authorizedPasswords then
231     for i,password in pairs(obj.authorizedPasswords) do
232       local user = password.user
233       if not user or string.len(user) == 0 then
234         user = "user-" .. UCI.random_string(6)
235       end
236
237       UCI.cursor_section(cursor, "cjdns", "password", nil, {
238         password = password.password,
239         user = user,
240         contact = password.contact
241       })
242     end
243   end
244
245   return cursor:save("cjdns")
246 end
247
248 --- Simple backport of Cursor:section from luci.model.uci
249 --
250 -- Backport reason: we don't wanna depend on LuCI.
251 -- @param Cursor the UCI cursor to operate on
252 -- @param string name of the config
253 -- @param string type of the section
254 -- @param string name of the section (optional)
255 -- @param table config values
256 function UCI.cursor_section(cursor, config, type, section, values)
257   if section then
258     cursor:set(config, section, type)
259   else
260     section = cursor:add("cjdns", type)
261   end
262
263   for k,v in pairs(values) do
264     cursor:set(config, section, k, v)
265   end
266 end
267
268 function UCI.makeInterface()
269   local cursor = uci.cursor()
270
271   local config = cursor:get_all("cjdns", "cjdns")
272   if not config then return nil end
273
274   return common.AdminInterface.new({
275     host = config.admin_address,
276     port = config.admin_port,
277     password = config.admin_password,
278     config = UCI.get(),
279     timeout = 2
280   })
281 end
282
283 function UCI.random_string(length)
284   -- tr -cd 'A-Za-z0-9' < /dev/urandom
285   local urandom = io.popen("tr -cd 'A-Za-z0-9' 2> /dev/null < /dev/urandom", "r")
286   local string = urandom:read(length)
287   urandom:close()
288   return string
289 end