Mercurial > prosody-hg
comparison plugins/mod_s2s/mod_s2s.lua @ 6054:7a5ddbaf758d
Merge 0.9->0.10
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Wed, 02 Apr 2014 17:41:38 +0100 |
| parents | d21ea6001bba |
| children | e626ee2fe106 |
comparison
equal
deleted
inserted
replaced
| 6053:2f93a04564b2 | 6054:7a5ddbaf758d |
|---|---|
| 1 -- Prosody IM | 1 -- Prosody IM |
| 2 -- Copyright (C) 2008-2010 Matthew Wild | 2 -- Copyright (C) 2008-2010 Matthew Wild |
| 3 -- Copyright (C) 2008-2010 Waqas Hussain | 3 -- Copyright (C) 2008-2010 Waqas Hussain |
| 4 -- | 4 -- |
| 5 -- This project is MIT/X11 licensed. Please see the | 5 -- This project is MIT/X11 licensed. Please see the |
| 6 -- COPYING file in the source package for more information. | 6 -- COPYING file in the source package for more information. |
| 7 -- | 7 -- |
| 8 | 8 |
| 9 module:set_global(); | 9 module:set_global(); |
| 133 return false; | 133 return false; |
| 134 end | 134 end |
| 135 return true; | 135 return true; |
| 136 end | 136 end |
| 137 | 137 |
| 138 local function keepalive(event) | |
| 139 return event.session.sends2s(' '); | |
| 140 end | |
| 141 | |
| 142 module:hook("s2s-read-timeout", keepalive, -1); | |
| 143 | |
| 138 function module.add_host(module) | 144 function module.add_host(module) |
| 139 if module:get_option_boolean("disallow_s2s", false) then | 145 if module:get_option_boolean("disallow_s2s", false) then |
| 140 module:log("warn", "The 'disallow_s2s' config option is deprecated, please see http://prosody.im/doc/s2s#disabling"); | 146 module:log("warn", "The 'disallow_s2s' config option is deprecated, please see http://prosody.im/doc/s2s#disabling"); |
| 141 return nil, "This host has disallow_s2s set"; | 147 return nil, "This host has disallow_s2s set"; |
| 142 end | 148 end |
| 143 module:hook("route/remote", route_to_existing_session, -1); | 149 module:hook("route/remote", route_to_existing_session, -1); |
| 144 module:hook("route/remote", route_to_new_session, -10); | 150 module:hook("route/remote", route_to_new_session, -10); |
| 145 module:hook("s2s-authenticated", make_authenticated, -1); | 151 module:hook("s2s-authenticated", make_authenticated, -1); |
| 152 module:hook("s2s-read-timeout", keepalive, -1); | |
| 146 end | 153 end |
| 147 | 154 |
| 148 -- Stream is authorised, and ready for normal stanzas | 155 -- Stream is authorised, and ready for normal stanzas |
| 149 function mark_connected(session) | 156 function mark_connected(session) |
| 150 local sendq, send = session.sendq, session.sends2s; | 157 local sendq, send = session.sendq, session.sends2s; |
| 151 | 158 |
| 152 local from, to = session.from_host, session.to_host; | 159 local from, to = session.from_host, session.to_host; |
| 153 | 160 |
| 154 session.log("info", "%s s2s connection %s->%s complete", session.direction, from, to); | 161 session.log("info", "%s s2s connection %s->%s complete", session.direction:gsub("^.", string.upper), from, to); |
| 155 | 162 |
| 156 local event_data = { session = session }; | 163 local event_data = { session = session }; |
| 157 if session.type == "s2sout" then | 164 if session.type == "s2sout" then |
| 158 fire_global_event("s2sout-established", event_data); | 165 fire_global_event("s2sout-established", event_data); |
| 159 hosts[from].events.fire_event("s2sout-established", event_data); | 166 hosts[from].events.fire_event("s2sout-established", event_data); |
| 164 end; | 171 end; |
| 165 | 172 |
| 166 fire_global_event("s2sin-established", event_data); | 173 fire_global_event("s2sin-established", event_data); |
| 167 hosts[to].events.fire_event("s2sin-established", event_data); | 174 hosts[to].events.fire_event("s2sin-established", event_data); |
| 168 end | 175 end |
| 169 | 176 |
| 170 if session.direction == "outgoing" then | 177 if session.direction == "outgoing" then |
| 171 if sendq then | 178 if sendq then |
| 172 session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host); | 179 session.log("debug", "sending %d queued stanzas across new outgoing connection to %s", #sendq, session.to_host); |
| 173 for i, data in ipairs(sendq) do | 180 for i, data in ipairs(sendq) do |
| 174 send(data[1]); | 181 send(data[1]); |
| 175 sendq[i] = nil; | 182 sendq[i] = nil; |
| 176 end | 183 end |
| 177 session.sendq = nil; | 184 session.sendq = nil; |
| 178 end | 185 end |
| 179 | 186 |
| 180 session.ip_hosts = nil; | 187 session.ip_hosts = nil; |
| 181 session.srv_hosts = nil; | 188 session.srv_hosts = nil; |
| 182 end | 189 end |
| 183 end | 190 end |
| 184 | 191 |
| 209 session.hosts[host].authed = true; | 216 session.hosts[host].authed = true; |
| 210 else | 217 else |
| 211 return false; | 218 return false; |
| 212 end | 219 end |
| 213 session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host); | 220 session.log("debug", "connection %s->%s is now authenticated for %s", session.from_host, session.to_host, host); |
| 214 | 221 |
| 215 mark_connected(session); | 222 mark_connected(session); |
| 216 | 223 |
| 217 return true; | 224 return true; |
| 218 end | 225 end |
| 219 | 226 |
| 220 --- Helper to check that a session peer's certificate is valid | 227 --- Helper to check that a session peer's certificate is valid |
| 221 local function check_cert_status(session) | 228 local function check_cert_status(session) |
| 268 | 275 |
| 269 local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; | 276 local xmlns_xmpp_streams = "urn:ietf:params:xml:ns:xmpp-streams"; |
| 270 | 277 |
| 271 function stream_callbacks.streamopened(session, attr) | 278 function stream_callbacks.streamopened(session, attr) |
| 272 local send = session.sends2s; | 279 local send = session.sends2s; |
| 273 | 280 |
| 274 session.version = tonumber(attr.version) or 0; | 281 session.version = tonumber(attr.version) or 0; |
| 275 | 282 |
| 276 -- TODO: Rename session.secure to session.encrypted | 283 -- TODO: Rename session.secure to session.encrypted |
| 277 if session.secure == false then | 284 if session.secure == false then |
| 278 session.secure = true; | 285 session.secure = true; |
| 279 | 286 session.encrypted = true; |
| 280 -- Check if TLS compression is used | 287 |
| 281 local sock = session.conn:socket(); | 288 local sock = session.conn:socket(); |
| 282 if sock.info then | 289 if sock.info then |
| 283 session.compressed = sock:info"compression"; | 290 local info = sock:info(); |
| 284 elseif sock.compression then | 291 (session.log or log)("info", "Stream encrypted (%s with %s)", info.protocol, info.cipher); |
| 285 session.compressed = sock:compression(); --COMPAT mw/luasec-hg | 292 session.compressed = info.compression; |
| 293 else | |
| 294 (session.log or log)("info", "Stream encrypted"); | |
| 295 session.compressed = sock.compression and sock:compression(); --COMPAT mw/luasec-hg | |
| 286 end | 296 end |
| 287 end | 297 end |
| 288 | 298 |
| 289 if session.direction == "incoming" then | 299 if session.direction == "incoming" then |
| 290 -- Send a reply stream header | 300 -- Send a reply stream header |
| 291 | 301 |
| 292 -- Validate to/from | 302 -- Validate to/from |
| 293 local to, from = nameprep(attr.to), nameprep(attr.from); | 303 local to, from = nameprep(attr.to), nameprep(attr.from); |
| 294 if not to and attr.to then -- COMPAT: Some servers do not reliably set 'to' (especially on stream restarts) | 304 if not to and attr.to then -- COMPAT: Some servers do not reliably set 'to' (especially on stream restarts) |
| 295 session:close({ condition = "improper-addressing", text = "Invalid 'to' address" }); | 305 session:close({ condition = "improper-addressing", text = "Invalid 'to' address" }); |
| 296 return; | 306 return; |
| 297 end | 307 end |
| 298 if not from and attr.from then -- COMPAT: Some servers do not reliably set 'from' (especially on stream restarts) | 308 if not from and attr.from then -- COMPAT: Some servers do not reliably set 'from' (especially on stream restarts) |
| 299 session:close({ condition = "improper-addressing", text = "Invalid 'from' address" }); | 309 session:close({ condition = "improper-addressing", text = "Invalid 'from' address" }); |
| 300 return; | 310 return; |
| 301 end | 311 end |
| 302 | 312 |
| 303 -- Set session.[from/to]_host if they have not been set already and if | 313 -- Set session.[from/to]_host if they have not been set already and if |
| 304 -- this session isn't already authenticated | 314 -- this session isn't already authenticated |
| 305 if session.type == "s2sin_unauthed" and from and not session.from_host then | 315 if session.type == "s2sin_unauthed" and from and not session.from_host then |
| 306 session.from_host = from; | 316 session.from_host = from; |
| 307 elseif from ~= session.from_host then | 317 elseif from ~= session.from_host then |
| 312 session.to_host = to; | 322 session.to_host = to; |
| 313 elseif to ~= session.to_host then | 323 elseif to ~= session.to_host then |
| 314 session:close({ condition = "improper-addressing", text = "New stream 'to' attribute does not match original" }); | 324 session:close({ condition = "improper-addressing", text = "New stream 'to' attribute does not match original" }); |
| 315 return; | 325 return; |
| 316 end | 326 end |
| 317 | 327 |
| 318 -- For convenience we'll put the sanitised values into these variables | 328 -- For convenience we'll put the sanitised values into these variables |
| 319 to, from = session.to_host, session.from_host; | 329 to, from = session.to_host, session.from_host; |
| 320 | 330 |
| 321 session.streamid = uuid_gen(); | 331 session.streamid = uuid_gen(); |
| 322 (session.log or log)("debug", "Incoming s2s received %s", st.stanza("stream:stream", attr):top_tag()); | 332 (session.log or log)("debug", "Incoming s2s received %s", st.stanza("stream:stream", attr):top_tag()); |
| 323 if to then | 333 if to then |
| 324 if not hosts[to] then | 334 if not hosts[to] then |
| 325 -- Attempting to connect to a host we don't serve | 335 -- Attempting to connect to a host we don't serve |
| 350 end | 360 end |
| 351 | 361 |
| 352 session:open_stream(session.to_host, session.from_host) | 362 session:open_stream(session.to_host, session.from_host) |
| 353 if session.version >= 1.0 then | 363 if session.version >= 1.0 then |
| 354 local features = st.stanza("stream:features"); | 364 local features = st.stanza("stream:features"); |
| 355 | 365 |
| 356 if to then | 366 if to then |
| 357 hosts[to].events.fire_event("s2s-stream-features", { origin = session, features = features }); | 367 hosts[to].events.fire_event("s2s-stream-features", { origin = session, features = features }); |
| 358 else | 368 else |
| 359 (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"); | 369 (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"); |
| 360 end | 370 end |
| 361 | 371 |
| 362 log("debug", "Sending stream features: %s", tostring(features)); | 372 log("debug", "Sending stream features: %s", tostring(features)); |
| 363 send(features); | 373 send(features); |
| 364 end | 374 end |
| 365 elseif session.direction == "outgoing" then | 375 elseif session.direction == "outgoing" then |
| 366 -- If we are just using the connection for verifying dialback keys, we won't try and auth it | 376 -- If we are just using the connection for verifying dialback keys, we won't try and auth it |
| 384 session.sends2s(tostring(data)); | 394 session.sends2s(tostring(data)); |
| 385 send_buffer[i] = nil; | 395 send_buffer[i] = nil; |
| 386 end | 396 end |
| 387 end | 397 end |
| 388 session.send_buffer = nil; | 398 session.send_buffer = nil; |
| 389 | 399 |
| 390 -- If server is pre-1.0, don't wait for features, just do dialback | 400 -- If server is pre-1.0, don't wait for features, just do dialback |
| 391 if session.version < 1.0 then | 401 if session.version < 1.0 then |
| 392 if not session.dialback_verifying then | 402 if not session.dialback_verifying then |
| 393 hosts[session.from_host].events.fire_event("s2sout-authenticate-legacy", { origin = session }); | 403 hosts[session.from_host].events.fire_event("s2sout-authenticate-legacy", { origin = session }); |
| 394 else | 404 else |
| 477 end | 487 end |
| 478 end | 488 end |
| 479 | 489 |
| 480 session.sends2s("</stream:stream>"); | 490 session.sends2s("</stream:stream>"); |
| 481 function session.sends2s() return false; end | 491 function session.sends2s() return false; end |
| 482 | 492 |
| 483 local reason = remote_reason or (reason and (reason.text or reason.condition)) or reason; | 493 local reason = remote_reason or (reason and (reason.text or reason.condition)) or reason; |
| 484 session.log("info", "%s s2s stream %s->%s closed: %s", session.direction, session.from_host or "(unknown host)", session.to_host or "(unknown host)", reason or "stream closed"); | 494 session.log("info", "%s s2s stream %s->%s closed: %s", session.direction:gsub("^.", string.upper), session.from_host or "(unknown host)", session.to_host or "(unknown host)", reason or "stream closed"); |
| 485 | 495 |
| 486 -- Authenticated incoming stream may still be sending us stanzas, so wait for </stream:stream> from remote | 496 -- Authenticated incoming stream may still be sending us stanzas, so wait for </stream:stream> from remote |
| 487 local conn = session.conn; | 497 local conn = session.conn; |
| 488 if reason == nil and not session.notopen and session.type == "s2sin" then | 498 if reason == nil and not session.notopen and session.type == "s2sin" then |
| 489 add_task(stream_close_timeout, function () | 499 add_task(stream_close_timeout, function () |
| 490 if not session.destroyed then | 500 if not session.destroyed then |
| 520 | 530 |
| 521 -- Session initialization logic shared by incoming and outgoing | 531 -- Session initialization logic shared by incoming and outgoing |
| 522 local function initialize_session(session) | 532 local function initialize_session(session) |
| 523 local stream = new_xmpp_stream(session, stream_callbacks); | 533 local stream = new_xmpp_stream(session, stream_callbacks); |
| 524 session.stream = stream; | 534 session.stream = stream; |
| 525 | 535 |
| 526 session.notopen = true; | 536 session.notopen = true; |
| 527 | 537 |
| 528 function session.reset_stream() | 538 function session.reset_stream() |
| 529 session.notopen = true; | 539 session.notopen = true; |
| 530 session.stream:reset(); | 540 session.stream:reset(); |
| 531 end | 541 end |
| 532 | 542 |
| 533 session.open_stream = session_open_stream; | 543 session.open_stream = session_open_stream; |
| 534 | 544 |
| 535 local filter = session.filter; | 545 local filter = session.filter; |
| 536 function session.data(data) | 546 function session.data(data) |
| 537 data = filter("bytes/in", data); | 547 data = filter("bytes/in", data); |
| 538 if data then | 548 if data then |
| 539 local ok, err = stream:feed(data); | 549 local ok, err = stream:feed(data); |
| 584 if t then | 594 if t then |
| 585 return w(conn, t); | 595 return w(conn, t); |
| 586 end | 596 end |
| 587 end | 597 end |
| 588 end | 598 end |
| 589 | 599 |
| 590 initialize_session(session); | 600 initialize_session(session); |
| 591 else -- Outgoing session connected | 601 else -- Outgoing session connected |
| 592 session:open_stream(session.from_host, session.to_host); | 602 session:open_stream(session.from_host, session.to_host); |
| 593 end | 603 end |
| 604 session.ip = conn:ip(); | |
| 594 end | 605 end |
| 595 | 606 |
| 596 function listener.onincoming(conn, data) | 607 function listener.onincoming(conn, data) |
| 597 local session = sessions[conn]; | 608 local session = sessions[conn]; |
| 598 if session then | 609 if session then |
| 599 session.data(data); | 610 session.data(data); |
| 600 end | 611 end |
| 601 end | 612 end |
| 602 | 613 |
| 603 function listener.onstatus(conn, status) | 614 function listener.onstatus(conn, status) |
| 604 if status == "ssl-handshake-complete" then | 615 if status == "ssl-handshake-complete" then |
| 605 local session = sessions[conn]; | 616 local session = sessions[conn]; |
| 606 if session and session.direction == "outgoing" then | 617 if session and session.direction == "outgoing" then |
| 607 session.log("debug", "Sending stream header..."); | 618 session.log("debug", "Sending stream header..."); |
| 615 if session then | 626 if session then |
| 616 sessions[conn] = nil; | 627 sessions[conn] = nil; |
| 617 if err and session.direction == "outgoing" and session.notopen then | 628 if err and session.direction == "outgoing" and session.notopen then |
| 618 (session.log or log)("debug", "s2s connection attempt failed: %s", err); | 629 (session.log or log)("debug", "s2s connection attempt failed: %s", err); |
| 619 if s2sout.attempt_connection(session, err) then | 630 if s2sout.attempt_connection(session, err) then |
| 620 (session.log or log)("debug", "...so we're going to try another target"); | |
| 621 return; -- Session lives for now | 631 return; -- Session lives for now |
| 622 end | 632 end |
| 623 end | 633 end |
| 624 (session.log or log)("debug", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "connection closed")); | 634 (session.log or log)("debug", "s2s disconnected: %s->%s (%s)", tostring(session.from_host), tostring(session.to_host), tostring(err or "connection closed")); |
| 625 s2s_destroy_session(session, err); | 635 s2s_destroy_session(session, err); |
| 636 end | |
| 637 end | |
| 638 | |
| 639 function listener.onreadtimeout(conn) | |
| 640 local session = sessions[conn]; | |
| 641 if session then | |
| 642 return (hosts[session.host] or prosody).events.fire_event("s2s-read-timeout", { session = session }); | |
| 626 end | 643 end |
| 627 end | 644 end |
| 628 | 645 |
| 629 function listener.register_outgoing(conn, session) | 646 function listener.register_outgoing(conn, session) |
| 630 session.direction = "outgoing"; | 647 session.direction = "outgoing"; |
| 639 if not must_secure and secure_domains[host] then | 656 if not must_secure and secure_domains[host] then |
| 640 must_secure = true; | 657 must_secure = true; |
| 641 elseif must_secure and insecure_domains[host] then | 658 elseif must_secure and insecure_domains[host] then |
| 642 must_secure = false; | 659 must_secure = false; |
| 643 end | 660 end |
| 644 | 661 |
| 645 if must_secure and (session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid") then | 662 if must_secure and (session.cert_chain_status ~= "valid" or session.cert_identity_status ~= "valid") then |
| 646 module:log("warn", "Forbidding insecure connection to/from %s", host or session.ip or "(unknown host)"); | 663 module:log("warn", "Forbidding insecure connection to/from %s", host or session.ip or "(unknown host)"); |
| 647 if session.direction == "incoming" then | 664 if session.direction == "incoming" then |
| 648 session:close({ condition = "not-authorized", text = "Your server's certificate is invalid, expired, or not trusted by "..session.to_host }); | 665 session:close({ condition = "not-authorized", text = "Your server's certificate is invalid, expired, or not trusted by "..session.to_host }); |
| 649 else -- Close outgoing connections without warning | 666 else -- Close outgoing connections without warning |
