1 local config
= require
"core.configmanager";
2 local certmanager
= require
"core.certmanager";
3 local server
= require
"net.server";
4 local socket
= require
"socket";
6 local log = require
"util.logger".init("portmanager");
7 local multitable
= require
"util.multitable";
8 local set
= require
"util.set";
11 local setmetatable
, rawset, rawget = setmetatable
, rawset, rawget;
12 local type, tonumber, ipairs
= type, tonumber, ipairs
;
15 local prosody
= prosody
;
16 local fire_event
= prosody
.events
.fire_event
;
23 local default_interfaces
= { };
24 local default_local_interfaces
= { };
25 if config
.get("*", "use_ipv4") ~= false then
26 table.insert(default_interfaces
, "*");
27 table.insert(default_local_interfaces
, "127.0.0.1");
29 if socket
.tcp6
and config
.get("*", "use_ipv6") ~= false then
30 table.insert(default_interfaces
, "::");
31 table.insert(default_local_interfaces
, "::1");
34 local default_mode
= config
.get("*", "network_default_read_size") or 4096;
38 -- service_name -> { service_info, ... }
39 local services
= setmetatable({}, { __index
= function (t
, k
) rawset(t
, k
, {}); return rawget(t
, k
); end });
41 -- service_name, interface (string), port (number)
42 local active_services
= multitable
.new();
46 local function error_to_friendly_message(service_name
, port
, err
) --luacheck: ignore 212/service_name
47 local friendly_message
= err
;
48 if err
:match(" in use") then
49 -- FIXME: Use service_name here
50 if port
== 5222 or port
== 5223 or port
== 5269 then
51 friendly_message
= "check that Prosody or another XMPP server is "
52 .."not already running and using this port";
53 elseif port
== 80 or port
== 81 then
54 friendly_message
= "check that a HTTP server is not already using "
56 elseif port
== 5280 then
57 friendly_message
= "check that Prosody or a BOSH connection manager "
58 .."is not already running";
60 friendly_message
= "this port is in use by another application";
62 elseif err
:match("permission") then
63 friendly_message
= "Prosody does not have sufficient privileges to use this port";
65 return friendly_message
;
70 local function activate(service_name
)
71 local service_info
= services
[service_name
][1];
72 if not service_info
then
73 return nil, "Unknown service: "..service_name
;
76 local listener
= service_info
.listener
;
78 local config_prefix
= (service_info
.config_prefix
or service_name
).."_";
79 if config_prefix
== "_" then
83 local bind_interfaces
= config
.get("*", config_prefix
.."interfaces")
84 or config
.get("*", config_prefix
.."interface") -- COMPAT w/pre-0.9
85 or (service_info
.private
and (config
.get("*", "local_interfaces") or default_local_interfaces
))
86 or config
.get("*", "interfaces")
87 or config
.get("*", "interface") -- COMPAT w/pre-0.9
88 or listener
.default_interface
-- COMPAT w/pre0.9
90 bind_interfaces
= set
.new(type(bind_interfaces
)~="table" and {bind_interfaces
} or bind_interfaces
);
92 local bind_ports
= config
.get("*", config_prefix
.."ports")
93 or service_info
.default_ports
94 or {service_info
.default_port
95 or listener
.default_port
-- COMPAT w/pre-0.9
97 bind_ports
= set
.new(type(bind_ports
) ~= "table" and { bind_ports
} or bind_ports
);
99 local mode
= listener
.default_mode
or default_mode
;
100 local hooked_ports
= {};
102 for interface
in bind_interfaces
do
103 for port
in bind_ports
do
104 local port_number
= tonumber(port
);
105 if not port_number
then
106 log("error", "Invalid port number specified for service '%s': %s", service_info
.name
, port
);
107 elseif #active_services
:search(nil, interface
, port_number
) > 0 then
108 log("error", "Multiple services configured to listen on the same port ([%s]:%d): %s, %s", interface
, port
,
109 active_services
:search(nil, interface
, port
)[1][1].service
.name
or "<unnamed>", service_name
or "<unnamed>");
112 -- Create SSL context for this service/port
113 if service_info
.encryption
== "ssl" then
114 local global_ssl_config
= config
.get("*", "ssl") or {};
115 local prefix_ssl_config
= config
.get("*", config_prefix
.."ssl") or global_ssl_config
;
116 log("debug", "Creating context for direct TLS service %s on port %d", service_info
.name
, port
);
117 ssl
, err
, cfg
= certmanager
.create_context(service_info
.name
.." port "..port
, "server",
118 prefix_ssl_config
[interface
],
119 prefix_ssl_config
[port
],
121 service_info
.ssl_config
or {},
122 global_ssl_config
[interface
],
123 global_ssl_config
[port
]);
125 log("error", "Error binding encrypted port for %s: %s", service_info
.name
,
126 error_to_friendly_message(service_name
, port_number
, err
) or "unknown error");
130 -- Start listening on interface+port
131 local handler
, err
= server
.listen(interface
, port_number
, listener
, {
134 tls_direct
= service_info
.encryption
== "ssl";
138 log("error", "Failed to open server port %d on %s, %s", port_number
, interface
,
139 error_to_friendly_message(service_name
, port_number
, err
));
141 table.insert(hooked_ports
, "["..interface
.."]:"..port_number
);
142 log("debug", "Added listening service %s to [%s]:%d", service_name
, interface
, port_number
);
143 active_services
:add(service_name
, interface
, port_number
, {
145 service
= service_info
;
153 log("info", "Activated service '%s' on %s", service_name
,
154 #hooked_ports
== 0 and "no ports" or table.concat(hooked_ports
, ", "));
158 local close
; -- forward declaration
160 local function deactivate(service_name
, service_info
)
161 for name
, interface
, port
, n
, active_service
--luacheck: ignore 213/name 213/n
162 in active_services
:iter(service_name
or service_info
and service_info
.name
, nil, nil, nil) do
163 if service_info
== nil or active_service
.service
== service_info
then
164 close(interface
, port
);
167 log("info", "Deactivated service '%s'", service_name
or service_info
.name
);
170 local function register_service(service_name
, service_info
)
171 table.insert(services
[service_name
], service_info
);
173 if not active_services
:get(service_name
) then
174 log("debug", "No active service for %s, activating...", service_name
);
175 local ok
, err
= activate(service_name
);
177 log("error", "Failed to activate service '%s': %s", service_name
, err
or "unknown error");
181 fire_event("service-added", { name
= service_name
, service
= service_info
});
185 local function unregister_service(service_name
, service_info
)
186 log("debug", "Unregistering service: %s", service_name
);
187 local service_info_list
= services
[service_name
];
188 for i
, service
in ipairs(service_info_list
) do
189 if service
== service_info
then
190 table.remove(service_info_list
, i
);
193 deactivate(nil, service_info
);
194 if #service_info_list
> 0 then -- Other services registered with this name
195 activate(service_name
); -- Re-activate with the next available one
197 fire_event("service-removed", { name
= service_name
, service
= service_info
});
200 local get_service_at
-- forward declaration
202 function close(interface
, port
)
203 local service
, service_server
= get_service_at(interface
, port
);
205 return false, "port-not-open";
207 service_server
:close();
208 active_services
:remove(service
.name
, interface
, port
);
209 log("debug", "Removed listening service %s from [%s]:%d", service
.name
, interface
, port
);
213 function get_service_at(interface
, port
)
214 local data
= active_services
:search(nil, interface
, port
)[1][1];
215 return data
.service
, data
.server
;
218 local function get_service(service_name
)
219 return (services
[service_name
] or {})[1];
222 local function get_active_services()
223 return active_services
;
226 local function get_registered_services()
232 local function add_sni_host(host
, service
)
233 -- local global_ssl_config = config.get(host, "ssl") or {};
234 for name
, interface
, port
, n
, active_service
--luacheck: ignore 213
235 in active_services
:iter(service
, nil, nil, nil) do
236 if active_service
.server
.hosts
and active_service
.tls_cfg
then
237 -- local config_prefix = (active_service.config_prefix or name).."_";
238 -- if config_prefix == "_" then
239 -- config_prefix = "";
241 -- local prefix_ssl_config = config.get(host, config_prefix.."ssl") or global_ssl_config;
242 -- FIXME only global 'ssl' settings are mixed in here
243 -- TODO per host and per service settings should be merged in,
244 -- without overriding the per-host certificate
245 local ssl
, err
, cfg
= certmanager
.create_context(host
, "server");
247 active_service
.server
.hosts
[host
] = ssl
;
248 if not active_service
.tls_cfg
.certificate
then
249 active_service
.server
.tls_ctx
= ssl
;
250 active_service
.tls_cfg
= cfg
;
253 log("error", "err = %q", err
);
258 prosody
.events
.add_handler("item-added/net-provider", function (event
)
259 local item
= event
.item
;
260 register_service(item
.name
, item
);
261 for host
in pairs(prosody
.hosts
) do
262 add_sni_host(host
, item
.name
);
265 prosody
.events
.add_handler("item-removed/net-provider", function (event
)
266 local item
= event
.item
;
267 unregister_service(item
.name
, item
);
270 prosody
.events
.add_handler("host-activated", add_sni_host
);
271 prosody
.events
.add_handler("host-deactivated", function (host
)
272 for name
, interface
, port
, n
, active_service
--luacheck: ignore 213
273 in active_services
:iter(nil, nil, nil, nil) do
274 if active_service
.tls_cfg
then
275 active_service
.server
.hosts
[host
] = nil;
282 deactivate
= deactivate
;
283 register_service
= register_service
;
284 unregister_service
= unregister_service
;
286 get_service_at
= get_service_at
;
287 get_service
= get_service
;
288 get_active_services
= get_active_services
;
289 get_registered_services
= get_registered_services
;