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