2 -- Copyright (C) 2008-2010 Matthew Wild
3 -- Copyright (C) 2008-2010 Waqas Hussain
5 -- This project is MIT/X11 licensed. Please see the
6 -- COPYING file in the source package for more information.
11 local prosody
= prosody
;
12 local hosts
= prosody
.hosts
;
13 local core_process_stanza
= prosody
.core_process_stanza
;
15 local tostring, type = tostring, type;
16 local t_insert
= table.insert
;
17 local traceback
= debug
.traceback
;
19 local add_task
= require
"util.timer".add_task
;
20 local st
= require
"util.stanza";
21 local initialize_filters
= require
"util.filters".initialize
;
22 local nameprep
= require
"util.encodings".stringprep
.nameprep
;
23 local new_xmpp_stream
= require
"util.xmppstream".new
;
24 local s2s_new_incoming
= require
"core.s2smanager".new_incoming
;
25 local s2s_new_outgoing
= require
"core.s2smanager".new_outgoing
;
26 local s2s_destroy_session
= require
"core.s2smanager".destroy_session
;
27 local uuid_gen
= require
"util.uuid".generate
;
28 local fire_global_event
= prosody
.events
.fire_event
;
29 local runner
= require
"util.async".runner
;
30 local connect
= require
"net.connect".connect
;
31 local service
= require
"net.resolvers.service";
33 local connect_timeout
= module
:get_option_number("s2s_timeout", 90);
34 local stream_close_timeout
= module
:get_option_number("s2s_close_timeout", 5);
35 local opt_keepalives
= module
:get_option_boolean("s2s_tcp_keepalives", module
:get_option_boolean("tcp_keepalives", true));
36 local secure_auth
= module
:get_option_boolean("s2s_secure_auth", false); -- One day...
37 local secure_domains
, insecure_domains
=
38 module
:get_option_set("s2s_secure_domains", {})._items
, module
:get_option_set("s2s_insecure_domains", {})._items
;
39 local require_encryption
= module
:get_option_boolean("s2s_require_encryption", false);
41 local measure_connections
= module
:measure("connections", "amount");
42 local measure_ipv6
= module
:measure("ipv6", "amount");
44 local sessions
= module
:shared("sessions");
46 local runner_callbacks
= {};
50 local log = module
._log
;
52 module
:hook("stats-update", function ()
55 for _
, session
in pairs(sessions
) do
57 if session
.ip
and session
.ip
:match(":") then
61 measure_connections(count
);
65 --- Handle stanzas to remote domains
67 local bouncy_stanzas
= { message
= true, presence
= true, iq
= true };
68 local function bounce_sendq(session
, reason
)
69 local sendq
= session
.sendq
;
70 if not sendq
then return; end
71 session
.log("info", "Sending error replies for %d queued stanzas because of failed outgoing connection to %s", #sendq
, session
.to_host
);
75 (session
.log or log)("error", "Replying to to an s2s error reply, please report this! Traceback: %s", traceback());
79 (session
.log or log)("error", "Attempting to close the dummy origin of s2s error replies, please report this! Traceback: %s", traceback());
82 -- FIXME Allow for more specific error conditions
83 -- TODO use util.error ?
84 local error_type
= "cancel";
85 local condition
= "remote-server-not-found";
86 if session
.had_stream
then -- set when a stream is opened by the remote
87 error_type
, condition
= "wait", "remote-server-timeout";
89 for i
, data
in ipairs(sendq
) do
90 local reply
= data
[2];
91 if reply
and not(reply
.attr
.xmlns
) and bouncy_stanzas
[reply
.name
] then
92 reply
.attr
.type = "error";
93 reply
:tag("error", {type = error_type
, by
= session
.from_host
})
94 :tag(condition
, {xmlns
= "urn:ietf:params:xml:ns:xmpp-stanzas"}):up();
96 reply
:tag("text", {xmlns
= "urn:ietf:params:xml:ns:xmpp-stanzas"})
97 :text("Server-to-server connection failed: "..reason
):up();
99 core_process_stanza(dummy
, reply
);
106 -- Handles stanzas to existing s2s sessions
107 function route_to_existing_session(event
)
108 local from_host
, to_host
, stanza
= event
.from_host
, event
.to_host
, event
.stanza
;
109 if not hosts
[from_host
] then
110 log("warn", "Attempt to send stanza from %s - a host we don't serve", from_host
);
113 if hosts
[to_host
] then
114 log("warn", "Attempt to route stanza to a remote %s - a host we do serve?!", from_host
);
117 local host
= hosts
[from_host
].s2sout
[to_host
];
119 -- We have a connection to this host already
120 if host
.type == "s2sout_unauthed" and (stanza
.name
~= "db:verify" or not host
.dialback_key
) then
121 (host
.log or log)("debug", "trying to send over unauthed s2sout to "..to_host
);
123 -- Queue stanza until we are able to send it
124 local queued_item
= {
126 stanza
.attr
.type ~= "error" and stanza
.attr
.type ~= "result" and st
.reply(stanza
);
129 t_insert(host
.sendq
, queued_item
);
131 -- luacheck: ignore 122
132 host
.sendq
= { queued_item
};
134 host
.log("debug", "stanza [%s] queued ", stanza
.name
);
136 elseif host
.type == "local" or host
.type == "component" then
137 log("error", "Trying to send a stanza to ourselves??")
138 log("error", "Traceback: %s", traceback());
139 log("error", "Stanza: %s", stanza
);
143 if host
.from_host
~= from_host
then
144 log("error", "WARNING! This might, possibly, be a bug, but it might not...");
145 log("error", "We are going to send from %s instead of %s", host
.from_host
, from_host
);
147 if host
.sends2s(stanza
) then
154 -- Create a new outgoing session for a stanza
155 function route_to_new_session(event
)
156 local from_host
, to_host
, stanza
= event
.from_host
, event
.to_host
, event
.stanza
;
157 log("debug", "opening a new outgoing connection for this stanza");
158 local host_session
= s2s_new_outgoing(from_host
, to_host
);
159 host_session
.version
= 1;
162 host_session
.bounce_sendq
= bounce_sendq
;
163 host_session
.sendq
= { {tostring(stanza
), stanza
.attr
.type ~= "error" and stanza
.attr
.type ~= "result" and st
.reply(stanza
)} };
164 log("debug", "stanza [%s] queued until connection complete", stanza
.name
);
165 connect(service
.new(to_host
, "xmpp-server", "tcp", { default_port
= 5269 }), listener
, nil, { session
= host_session
});
169 local function keepalive(event
)
170 return event
.session
.sends2s(' ');
173 module
:hook("s2s-read-timeout", keepalive
, -1);
175 function module
.add_host(module
)
176 if module
:get_option_boolean("disallow_s2s", false) then
177 module
:log("warn", "The 'disallow_s2s' config option is deprecated, please see https://prosody.im/doc/s2s#disabling");
178 return nil, "This host has disallow_s2s set";
180 module
:hook("route/remote", route_to_existing_session
, -1);
181 module
:hook("route/remote", route_to_new_session
, -10);
182 module
:hook("s2s-authenticated", make_authenticated
, -1);
183 module
:hook("s2s-read-timeout", keepalive
, -1);
184 module
:hook_stanza("http://etherx.jabber.org/streams", "features", function (session
, stanza
) -- luacheck: ignore 212/stanza
185 if session
.type == "s2sout" then
186 -- Stream is authenticated and we are seem to be done with feature negotiation,
187 -- so the stream is ready for stanzas. RFC 6120 Section 4.3
188 mark_connected(session
);
190 elseif not session
.dialback_verifying
then
191 session
.log("warn", "No SASL EXTERNAL offer and Dialback doesn't seem to be enabled, giving up");
198 -- Stream is authorised, and ready for normal stanzas
199 function mark_connected(session
)
201 local sendq
= session
.sendq
;
203 local from
, to
= session
.from_host
, session
.to_host
;
205 session
.log("info", "%s s2s connection %s->%s complete", session
.direction
:gsub("^.", string.upper
), from
, to
);
207 local event_data
= { session
= session
};
208 if session
.type == "s2sout" then
209 fire_global_event("s2sout-established", event_data
);
210 hosts
[from
].events
.fire_event("s2sout-established", event_data
);
212 local host_session
= hosts
[to
];
213 session
.send
= function(stanza
)
214 return host_session
.events
.fire_event("route/remote", { from_host
= to
, to_host
= from
, stanza
= stanza
});
217 fire_global_event("s2sin-established", event_data
);
218 hosts
[to
].events
.fire_event("s2sin-established", event_data
);
221 if session
.direction
== "outgoing" then
223 session
.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq
, session
.to_host
);
224 local send
= session
.sends2s
;
225 for i
, data
in ipairs(sendq
) do
234 function make_authenticated(event
)
235 local session
, host
= event
.session
, event
.host
;
236 if not session
.secure
then
237 if require_encryption
or (secure_auth
and not(insecure_domains
[host
])) or secure_domains
[host
] then
239 condition
= "policy-violation",
240 text
= "Encrypted server-to-server communication is required but was not "
241 ..((session
.direction
== "outgoing" and "offered") or "used")
246 session
:close({ condition
= "undefined-condition", text
= "Attempt to authenticate as a host we serve" });
248 if session
.type == "s2sout_unauthed" then
249 session
.type = "s2sout";
250 elseif session
.type == "s2sin_unauthed" then
251 session
.type = "s2sin";
252 elseif session
.type ~= "s2sin" and session
.type ~= "s2sout" then
256 if session
.incoming
and host
then
257 if not session
.hosts
[host
] then session
.hosts
[host
] = {}; end
258 session
.hosts
[host
].authed
= true;
260 session
.log("debug", "connection %s->%s is now authenticated for %s", session
.from_host
, session
.to_host
, host
);
262 if (session
.type == "s2sout" and session
.external_auth
~= "succeeded") or session
.type == "s2sin" then
263 -- Stream either used dialback for authentication or is an incoming stream.
264 mark_connected(session
);
270 --- Helper to check that a session peer's certificate is valid
271 function check_cert_status(session
)
272 local host
= session
.direction
== "outgoing" and session
.to_host
or session
.from_host
273 local conn
= session
.conn
:socket()
275 if conn
.getpeercertificate
then
276 cert
= conn
:getpeercertificate()
279 return module
:fire_event("s2s-check-certificate", { host
= host
, session
= session
, cert
= cert
});
282 --- XMPP stream event handlers
284 local stream_callbacks
= { default_ns
= "jabber:server" };
286 function stream_callbacks
.handlestanza(session
, stanza
)
287 stanza
= session
.filter("stanzas/in", stanza
);
288 session
.thread
:run(stanza
);
291 local xmlns_xmpp_streams
= "urn:ietf:params:xml:ns:xmpp-streams";
293 function stream_callbacks
.streamopened(session
, attr
)
294 -- run _streamopened in async context
295 session
.thread
:run({ attr
= attr
});
298 function stream_callbacks
._streamopened(session
, attr
)
299 session
.version
= tonumber(attr
.version
) or 0;
300 session
.had_stream
= true; -- Had a stream opened at least once
302 -- TODO: Rename session.secure to session.encrypted
303 if session
.secure
== false then
304 session
.secure
= true;
305 session
.encrypted
= true;
307 local sock
= session
.conn
:socket();
309 local info
= sock
:info();
310 (session
.log or log)("info", "Stream encrypted (%s with %s)", info
.protocol
, info
.cipher
);
311 session
.compressed
= info
.compression
;
313 (session
.log or log)("info", "Stream encrypted");
317 if session
.direction
== "incoming" then
318 -- Send a reply stream header
321 local to
, from
= nameprep(attr
.to
), nameprep(attr
.from
);
322 if not to
and attr
.to
then -- COMPAT: Some servers do not reliably set 'to' (especially on stream restarts)
323 session
:close({ condition
= "improper-addressing", text
= "Invalid 'to' address" });
326 if not from
and attr
.from
then -- COMPAT: Some servers do not reliably set 'from' (especially on stream restarts)
327 session
:close({ condition
= "improper-addressing", text
= "Invalid 'from' address" });
331 -- Set session.[from/to]_host if they have not been set already and if
332 -- this session isn't already authenticated
333 if session
.type == "s2sin_unauthed" and from
and not session
.from_host
then
334 session
.from_host
= from
;
335 elseif from
~= session
.from_host
then
336 session
:close({ condition
= "improper-addressing", text
= "New stream 'from' attribute does not match original" });
339 if session
.type == "s2sin_unauthed" and to
and not session
.to_host
then
340 session
.to_host
= to
;
341 elseif to
~= session
.to_host
then
342 session
:close({ condition
= "improper-addressing", text
= "New stream 'to' attribute does not match original" });
346 -- For convenience we'll put the sanitised values into these variables
347 to
, from
= session
.to_host
, session
.from_host
;
349 session
.streamid
= uuid_gen();
350 (session
.log or log)("debug", "Incoming s2s received %s", st
.stanza("stream:stream", attr
):top_tag());
352 if not hosts
[to
] then
353 -- Attempting to connect to a host we don't serve
355 condition
= "host-unknown";
356 text
= "This host does not serve "..to
359 elseif not hosts
[to
].modules
.s2s
then
360 -- Attempting to connect to a host that disallows s2s
362 condition
= "policy-violation";
363 text
= "Server-to-server communication is disabled for this host";
370 session
:close({ condition
= "undefined-condition", text
= "Attempt to connect from a host we serve" });
374 if session
.secure
and not session
.cert_chain_status
then
375 if check_cert_status(session
) == false then
380 session
:open_stream(session
.to_host
, session
.from_host
)
381 session
.notopen
= nil;
382 if session
.version
>= 1.0 then
383 local features
= st
.stanza("stream:features");
386 hosts
[to
].events
.fire_event("s2s-stream-features", { origin
= session
, features
= features
});
388 (session
.log or log)("warn", "No 'to' on stream header from %s means we can't offer any features", from
or session
.ip
or "unknown host");
389 fire_global_event("s2s-stream-features-legacy", { origin
= session
, features
= features
});
392 if ( session
.type == "s2sin" or session
.type == "s2sout" ) or features
.tags
[1] then
393 log("debug", "Sending stream features: %s", features
);
394 session
.sends2s(features
);
396 (session
.log or log)("warn", "No stream features to offer, giving up");
397 session
:close({ condition
= "undefined-condition", text
= "No stream features to offer" });
400 elseif session
.direction
== "outgoing" then
401 session
.notopen
= nil;
403 log("warn", "Stream response did not give us a stream id!");
404 session
:close({ condition
= "undefined-condition", text
= "Missing stream ID" });
407 session
.streamid
= attr
.id
;
409 if session
.secure
and not session
.cert_chain_status
then
410 if check_cert_status(session
) == false then
415 -- Send unauthed buffer
416 -- (stanzas which are fine to send before dialback)
417 -- Note that this is *not* the stanza queue (which
418 -- we can only send if auth succeeds) :)
419 local send_buffer
= session
.send_buffer
;
420 if send_buffer
and #send_buffer
> 0 then
421 log("debug", "Sending s2s send_buffer now...");
422 for i
, data
in ipairs(send_buffer
) do
423 session
.sends2s(tostring(data
));
424 send_buffer
[i
] = nil;
427 session
.send_buffer
= nil;
429 -- If server is pre-1.0, don't wait for features, just do dialback
430 if session
.version
< 1.0 then
431 if not session
.dialback_verifying
then
432 hosts
[session
.from_host
].events
.fire_event("s2sout-authenticate-legacy", { origin
= session
});
434 mark_connected(session
);
440 function stream_callbacks
.streamclosed(session
)
441 (session
.log or log)("debug", "Received </stream:stream>");
442 session
:close(false);
445 function stream_callbacks
.error(session
, error, data
)
446 if error == "no-stream" then
447 session
.log("debug", "Invalid opening stream header (%s)", (data
:gsub("^([^\1]+)\1", "{%1}")));
448 session
:close("invalid-namespace");
449 elseif error == "parse-error" then
450 session
.log("debug", "Server-to-server XML parse error: %s", error);
451 session
:close("not-well-formed");
452 elseif error == "stream-error" then
453 local condition
, text
= "undefined-condition";
454 for child
in data
:childtags(nil, xmlns_xmpp_streams
) do
455 if child
.name
~= "text" then
456 condition
= child
.name
;
458 text
= child
:get_text();
460 if condition
~= "undefined-condition" and text
then
464 text
= condition
.. (text
and (" ("..text
..")") or "");
465 session
.log("info", "Session closed by remote with error: %s", text
);
466 session
:close(nil, text
);
471 local stream_xmlns_attr
= {xmlns
='urn:ietf:params:xml:ns:xmpp-streams'};
472 local function session_close(session
, reason
, remote_reason
)
473 local log = session
.log or log;
475 if session
.notopen
then
476 if session
.direction
== "incoming" then
477 session
:open_stream(session
.to_host
, session
.from_host
);
479 session
:open_stream(session
.from_host
, session
.to_host
);
482 if reason
then -- nil == no err, initiated by us, false == initiated by remote
483 if type(reason
) == "string" then -- assume stream error
484 log("debug", "Disconnecting %s[%s], <stream:error> is: %s", session
.host
or session
.ip
or "(unknown host)", session
.type, reason
);
485 session
.sends2s(st
.stanza("stream:error"):tag(reason
, {xmlns
= 'urn:ietf:params:xml:ns:xmpp-streams' }));
486 elseif type(reason
) == "table" then
487 if reason
.condition
then
488 local stanza
= st
.stanza("stream:error"):tag(reason
.condition
, stream_xmlns_attr
):up();
490 stanza
:tag("text", stream_xmlns_attr
):text(reason
.text
):up();
493 stanza
:add_child(reason
.extra
);
495 log("debug", "Disconnecting %s[%s], <stream:error> is: %s",
496 session
.host
or session
.ip
or "(unknown host)", session
.type, stanza
);
497 session
.sends2s(stanza
);
498 elseif reason
.name
then -- a stanza
499 log("debug", "Disconnecting %s->%s[%s], <stream:error> is: %s",
500 session
.from_host
or "(unknown host)", session
.to_host
or "(unknown host)",
501 session
.type, reason
);
502 session
.sends2s(reason
);
507 session
.sends2s("</stream:stream>");
508 function session
.sends2s() return false; end
510 -- luacheck: ignore 422/reason
511 -- FIXME reason should be managed in a place common to c2s, s2s, bosh, component etc
512 local reason
= remote_reason
or (reason
and (reason
.text
or reason
.condition
)) or reason
;
513 session
.log("info", "%s s2s stream %s->%s closed: %s", session
.direction
:gsub("^.", string.upper
),
514 session
.from_host
or "(unknown host)", session
.to_host
or "(unknown host)", reason
or "stream closed");
516 -- Authenticated incoming stream may still be sending us stanzas, so wait for </stream:stream> from remote
517 local conn
= session
.conn
;
518 if reason
== nil and not session
.notopen
and session
.type == "s2sin" then
519 add_task(stream_close_timeout
, function ()
520 if not session
.destroyed
then
521 session
.log("warn", "Failed to receive a stream close response, closing connection anyway...");
522 s2s_destroy_session(session
, reason
);
527 s2s_destroy_session(session
, reason
);
528 conn
:close(); -- Close immediately, as this is an outgoing connection or is not authed
533 function session_stream_attrs(session
, from
, to
, attr
) -- luacheck: ignore 212/session
534 if not from
or (hosts
[from
] and hosts
[from
].modules
.dialback
) then
535 attr
["xmlns:db"] = 'jabber:server:dialback';
545 -- Session initialization logic shared by incoming and outgoing
546 local function initialize_session(session
)
547 local stream
= new_xmpp_stream(session
, stream_callbacks
);
549 session
.thread
= runner(function (stanza
)
550 if stanza
.name
== nil then
551 stream_callbacks
._streamopened(session
, stanza
.attr
);
553 core_process_stanza(session
, stanza
);
555 end, runner_callbacks
, session
);
557 local log = session
.log or log;
558 session
.stream
= stream
;
560 session
.notopen
= true;
562 function session
.reset_stream()
563 session
.notopen
= true;
564 session
.streamid
= nil;
565 session
.stream
:reset();
568 session
.stream_attrs
= session_stream_attrs
;
570 local filter
= initialize_filters(session
);
571 local conn
= session
.conn
;
572 local w
= conn
.write;
574 function session
.sends2s(t
)
575 log("debug", "Sending[%s]: %s", session
.type, t
.top_tag
and t
:top_tag() or t
:match("^[^>]*>?"));
577 t
= filter("stanzas/out", t
);
580 t
= filter("bytes/out", tostring(t
));
587 function session
.data(data
)
588 data
= filter("bytes/in", data
);
590 local ok
, err
= stream
:feed(data
);
591 if ok
then return; end
592 log("debug", "Received invalid XML (%s) %d bytes: %q", err
, #data
, data
:sub(1, 300));
593 session
:close("not-well-formed");
597 session
.close
= session_close
;
599 local handlestanza
= stream_callbacks
.handlestanza
;
600 function session
.dispatch_stanza(session
, stanza
) -- luacheck: ignore 432/session
601 return handlestanza(session
, stanza
);
604 module
:fire_event("s2s-created", { session
= session
});
606 add_task(connect_timeout
, function ()
607 if session
.type == "s2sin" or session
.type == "s2sout" then
608 return; -- Ok, we're connected
609 elseif session
.type == "s2s_destroyed" then
610 return; -- Session already destroyed
612 -- Not connected, need to close session and clean up
613 (session
.log or log)("debug", "Destroying incomplete session %s->%s due to inactivity",
614 session
.from_host
or "(unknown)", session
.to_host
or "(unknown)");
615 session
:close("connection-timeout");
619 function runner_callbacks
:ready()
620 self
.data
.log("debug", "Runner %s ready (%s)", self
.thread
, coroutine
.status(self
.thread
));
621 self
.data
.conn
:resume();
624 function runner_callbacks
:waiting()
625 self
.data
.log("debug", "Runner %s waiting (%s)", self
.thread
, coroutine
.status(self
.thread
));
626 self
.data
.conn
:pause();
629 function runner_callbacks
:error(err
)
630 (self
.data
.log or log)("error", "Traceback[s2s]: %s", err
);
633 function listener
.onconnect(conn
)
634 conn
:setoption("keepalive", opt_keepalives
);
635 local session
= sessions
[conn
];
636 if not session
then -- New incoming connection
637 session
= s2s_new_incoming(conn
);
638 sessions
[conn
] = session
;
639 session
.log("debug", "Incoming s2s connection");
640 initialize_session(session
);
641 else -- Outgoing session connected
642 session
:open_stream(session
.from_host
, session
.to_host
);
644 session
.ip
= conn
:ip();
647 function listener
.onincoming(conn
, data
)
648 local session
= sessions
[conn
];
654 function listener
.onstatus(conn
, status
)
655 if status
== "ssl-handshake-complete" then
656 local session
= sessions
[conn
];
657 if session
and session
.direction
== "outgoing" then
658 session
.log("debug", "Sending stream header...");
659 session
:open_stream(session
.from_host
, session
.to_host
);
664 function listener
.ondisconnect(conn
, err
)
665 local session
= sessions
[conn
];
667 sessions
[conn
] = nil;
668 (session
.log or log)("debug", "s2s disconnected: %s->%s (%s)", session
.from_host
, session
.to_host
, err
or "connection closed");
669 s2s_destroy_session(session
, err
);
673 function listener
.onfail(data
, err
)
674 local session
= data
and data
.session
;
676 if err
and session
.direction
== "outgoing" and session
.notopen
then
677 (session
.log or log)("debug", "s2s connection attempt failed: %s", err
);
679 (session
.log or log)("debug", "s2s disconnected: %s->%s (%s)", session
.from_host
, session
.to_host
, err
or "connection closed");
680 s2s_destroy_session(session
, err
);
684 function listener
.onreadtimeout(conn
)
685 local session
= sessions
[conn
];
687 local host
= session
.host
or session
.to_host
;
688 return (hosts
[host
] or prosody
).events
.fire_event("s2s-read-timeout", { session
= session
});
692 function listener
.register_outgoing(conn
, session
)
693 sessions
[conn
] = session
;
694 initialize_session(session
);
697 function listener
.ondetach(conn
)
698 sessions
[conn
] = nil;
701 function listener
.onattach(conn
, data
)
702 local session
= data
and data
.session
;
705 sessions
[conn
] = session
;
706 initialize_session(session
);
710 function check_auth_policy(event
)
711 local host
, session
= event
.host
, event
.session
;
712 local must_secure
= secure_auth
;
714 if not must_secure
and secure_domains
[host
] then
716 elseif must_secure
and insecure_domains
[host
] then
720 if must_secure
and (session
.cert_chain_status
~= "valid" or session
.cert_identity_status
~= "valid") then
721 module
:log("warn", "Forbidding insecure connection to/from %s", host
or session
.ip
or "(unknown host)");
722 if session
.direction
== "incoming" then
723 session
:close({ condition
= "not-authorized", text
= "Your server's certificate is invalid, expired, or not trusted by "..session
.to_host
});
724 else -- Close outgoing connections without warning
725 session
:close(false);
731 module
:hook("s2s-check-certificate", check_auth_policy
, -1);
733 module
:hook("server-stopping", function(event
)
734 local reason
= event
.reason
;
735 for _
, session
in pairs(sessions
) do
736 session
:close
{ condition
= "system-shutdown", text
= reason
};
742 module
:provides("net", {
746 encryption
= "starttls";
747 ssl_config
= { -- FIXME This is not used atm, see mod_tls
748 verify
= { "peer", "client_once", };
751 pattern
= "^<.*:stream.*%sxmlns%s*=%s*(['\"])jabber:server%1.*>";