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