2 LuCI - Lua Development Framework
4 Copyright 2009 Steven Barth <steven@midlink.org>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
15 local os = require "os"
16 local fs = require "nixio.fs"
17 local nixio = require "nixio"
18 local lucid = require "luci.lucid"
20 local ipairs, type, require, setmetatable = ipairs, type, require, setmetatable
21 local pairs, print, tostring, unpack = pairs, print, tostring, unpack
24 module "luci.lucid.tcpserver"
26 local cursor = lucid.cursor
27 local UCINAME = lucid.UCINAME
32 function prepare_daemon(config, server)
33 nixio.syslog("info", "Preparing TCP-Daemon " .. config[".name"])
34 if type(config.address) ~= "table" then
35 config.address = {config.address}
38 local sockets, socket, code, err = {}
39 local sopts = {reuseaddr = 1}
40 for _, addr in ipairs(config.address) do
41 local host, port = addr:match("(.-):?([^:]*)")
43 nixio.syslog("err", "Invalid address: " .. addr)
44 return nil, -5, "invalid address format"
45 elseif #host == 0 then
48 socket, code, err = prepare_socket(config.family, host, port, sopts)
50 sockets[#sockets+1] = socket
54 nixio.syslog("info", "Sockets bound for " .. config[".name"])
57 return nil, -6, "no sockets bound"
60 nixio.syslog("info", "Preparing publishers for " .. config[".name"])
63 for k, pname in ipairs(config.publisher) do
64 local pdata = cursor:get_all(UCINAME, pname)
66 publisher[#publisher+1] = pdata
68 nixio.syslog("err", "Publisher " .. pname .. " not found")
72 nixio.syslog("info", "Preparing TLS for " .. config[".name"])
74 local tls = prepare_tls(config.tls)
75 if not tls and config.encryption == "enable" then
76 for _, s in ipairs(sockets) do
79 return nil, -4, "Encryption requested, but no TLS context given"
82 nixio.syslog("info", "Invoking daemon factory for " .. config[".name"])
83 local handler, err = config.slave.module.factory(publisher, config)
85 for _, s in ipairs(sockets) do
90 local pollin = nixio.poll_flags("in")
91 for _, s in ipairs(sockets) do
92 server.register_pollfd({
99 publisher = publisher,
107 function accept(polle)
108 if not lucid.try_process() then
111 local socket, host, port = polle.fd:accept()
113 return nixio.syslog("warning", "accept() failed: " .. port)
116 socket:setblocking(true)
118 local function thread()
119 lucid.close_pollfds()
120 local inst = setmetatable({
121 host = host, port = port, interfaces = lucid.get_interfaces()
122 }, {__index = polle})
123 if polle.config.encryption then
124 socket = polle.tls:create(socket)
125 if not socket:accept() then
127 return nixio.syslog("warning", "TLS handshake failed: " .. host)
131 return polle.accept(socket, inst)
134 local stat = {lucid.create_process(thread)}
139 function prepare_socket(family, host, port, opts, backlog)
140 nixio.syslog("info", "Preparing socket for port " .. port)
141 backlog = backlog or 1024
142 family = family or "inetany"
145 local inetany = family == "inetany"
146 family = inetany and "inet6" or family
148 local socket, code, err = nixio.socket(family, "stream")
149 if not socket and inetany then
151 socket, code, err = nixio.socket(family, "stream")
155 return nil, code, err
158 for k, v in pairs(opts) do
159 socket:setsockopt("socket", k, v)
162 local stat, code, err = socket:bind(host, port)
164 return nil, code, err
167 stat, code, err = socket:listen(backlog)
169 return nil, code, err
172 socket:setblocking(false)
174 return socket, family
177 function prepare_tls(tlskey)
179 if tlskey and cursor:get(UCINAME, tlskey) then
180 tls = nixio.tls("server")
182 local make = cursor:get(UCINAME, tlskey, "generate") == "1"
183 local key = cursor:get(UCINAME, tlskey, "key")
184 local xtype = make and "asn1" or cursor:get(UCINAME, tlskey, "type")
185 local cert = cursor:get(UCINAME, tlskey, "cert")
186 local ciphers = cursor:get(UCINAME, tlskey, "ciphers")
188 if make and (not fs.access(key) or not fs.access(cert)) then
189 local CN = cursor:get(UCINAME, tlskey, "CN")
190 local O = cursor:get(UCINAME, tlskey, "O")
194 CN = CN or nixio.uname().nodename,
195 O = not O and "LuCId Keymaster" or #O > 0 and O
198 local stat, px5g = pcall(require, "px5g")
200 return nixio.syslog("err", "Unable to load PX5G Keymaster")
203 nixio.syslog("warning", "PX5G: Generating private key")
204 local rk = px5g.genkey(bits)
205 local keyfile = nixio.open(key, "w", 600)
206 if not rk or not keyfile or not keyfile:writeall(rk:asn1()) then
207 return nixio.syslog("err", "Unable to generate private key")
211 nixio.syslog("warning", "PX5G: Generating self-signed certificate")
212 if not fs.writefile(cert, rk:create_selfsigned(data,
213 os.time(), os.time() + 3600 * 24 * 366 * 15)) then
214 return nixio.syslog("err", "Unable to generate certificate")
219 if not tls:set_cert(cert, xtype) then
220 nixio.syslog("err", "Unable to load certificate: " .. cert)
224 if not tls:set_key(key, xtype) then
225 nixio.syslog("err", "Unable to load private key: " .. key)
230 if type(ciphers) == "table" then
231 ciphers = table.concat(ciphers, ":")
233 tls:set_ciphers(ciphers)