GSoC Commit #1: LuCId + HTTP-Server
[project/luci.git] / libs / lucid / luasrc / lucid / tcpserver.lua
1 --[[
2 LuCI - Lua Development Framework
3
4 Copyright 2009 Steven Barth <steven@midlink.org>
5
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
9
10 http://www.apache.org/licenses/LICENSE-2.0
11
12 $Id$
13 ]]
14
15 local os = require "os"
16 local nixio = require "nixio"
17 local lucid = require "luci.lucid"
18
19 local ipairs, type, require, setmetatable = ipairs, type, require, setmetatable
20 local pairs, print, tostring, unpack = pairs, print, tostring, unpack
21
22 module "luci.lucid.tcpserver"
23
24 local cursor = lucid.cursor
25 local UCINAME = lucid.UCINAME
26
27 local tcpsockets = {}
28
29
30 function prepare_daemon(config, server)
31 nixio.syslog("info", "Preparing TCP-Daemon " .. config[".name"])
32 if type(config.address) ~= "table" then
33 config.address = {config.address}
34 end
35
36 local sockets, socket, code, err = {}
37 local sopts = {reuseaddr = 1}
38 for _, addr in ipairs(config.address) do
39 local host, port = addr:match("(.-):?([^:]*)")
40 if not host then
41 nixio.syslog("err", "Invalid address: " .. addr)
42 return nil, -5, "invalid address format"
43 elseif #host == 0 then
44 host = nil
45 end
46 socket, code, err = prepare_socket(config.family, host, port, sopts)
47 if socket then
48 sockets[#sockets+1] = socket
49 end
50 end
51
52 nixio.syslog("info", "Sockets bound for " .. config[".name"])
53
54 if #sockets < 1 then
55 return nil, -6, "no sockets bound"
56 end
57
58 nixio.syslog("info", "Preparing publishers for " .. config[".name"])
59
60 local publisher = {}
61 for k, pname in ipairs(config.publisher) do
62 local pdata = cursor:get_all(UCINAME, pname)
63 if pdata then
64 publisher[#publisher+1] = pdata
65 else
66 nixio.syslog("err", "Publisher " .. pname .. " not found")
67 end
68 end
69
70 nixio.syslog("info", "Preparing TLS for " .. config[".name"])
71
72 local tls = prepare_tls(config.tls)
73 if not tls and config.encryption == "enable" then
74 for _, s in ipairs(sockets) do
75 s:close()
76 end
77 return nil, -4, "Encryption requested, but no TLS context given"
78 end
79
80 nixio.syslog("info", "Invoking daemon factory for " .. config[".name"])
81 local handler, err = config.slave.module.factory(publisher, config)
82 if not handler then
83 for _, s in ipairs(sockets) do
84 s:close()
85 end
86 return nil, -3, err
87 else
88 local pollin = nixio.poll_flags("in")
89 for _, s in ipairs(sockets) do
90 server.register_pollfd({
91 fd = s,
92 events = pollin,
93 revents = 0,
94 handler = accept,
95 accept = handler,
96 config = config,
97 publisher = publisher,
98 tls = tls
99 })
100 end
101 return true
102 end
103 end
104
105 function accept(polle)
106 local socket, host, port = polle.fd:accept()
107 if not socket then
108 return nixio.syslog("warn", "accept() failed: " .. port)
109 end
110
111 socket:setblocking(true)
112
113 local function thread()
114 lucid.close_pollfds()
115 local inst = setmetatable({
116 host = host, port = port, interfaces = lucid.get_interfaces()
117 }, {__index = polle})
118 if polle.config.encryption then
119 socket = polle.tls:create(socket)
120 if not socket:accept() then
121 socket:close()
122 return nixio.syslog("warning", "TLS handshake failed: " .. host)
123 end
124 end
125
126 return polle.accept(socket, inst)
127 end
128
129 local stat = {lucid.create_process(thread)}
130 socket:close()
131 return unpack(stat)
132 end
133
134 function prepare_socket(family, host, port, opts, backlog)
135 nixio.syslog("info", "Preparing socket for port " .. port)
136 backlog = backlog or 1024
137 family = family or "inetany"
138 opts = opts or {}
139
140 local inetany = family == "inetany"
141 family = inetany and "inet6" or family
142
143 local socket, code, err = nixio.socket(family, "stream")
144 if not socket and inetany then
145 family = "inet"
146 socket, code, err = nixio.socket(family, "stream")
147 end
148
149 if not socket then
150 return nil, code, err
151 end
152
153 for k, v in pairs(opts) do
154 socket:setsockopt("socket", k, v)
155 end
156
157 local stat, code, err = socket:bind(host, port)
158 if not stat then
159 return nil, code, err
160 end
161
162 stat, code, err = socket:listen(backlog)
163 if not stat then
164 return nil, code, err
165 end
166
167 socket:setblocking(false)
168
169 return socket, family
170 end
171
172 function prepare_tls(tlskey)
173 local tls = nixio.tls()
174 if tlskey and cursor:get(UCINAME, tlskey) then
175 local cert = cursor:get(UCINAME, tlskey, "cert")
176 if cert then
177 tls:set_cert(cert)
178 end
179 local key = cursor:get(UCINAME, tlskey, "key")
180 if key then
181 tls:set_key(key)
182 end
183 local ciphers = cursor:get(UCINAME, tlskey, "ciphers")
184 if ciphers then
185 if type(ciphers) == "table" then
186 ciphers = table.concat(ciphers, ":")
187 end
188 tls:set_ciphers(ciphers)
189 end
190 end
191 return tls
192 end