Software /
code /
prosody
Comparison
core/s2smanager.lua @ 4421:971a66627f7a
s2smanager, xmppserver_listener: Support for connecting to other servers via IPv6
author | Florian Zeitz <florob@babelmonkeys.de> |
---|---|
date | Sat, 22 Oct 2011 18:29:23 +0200 |
parent | 4329:d8b2c97ae6ed |
child | 4422:c25dee24623f |
comparison
equal
deleted
inserted
replaced
4420:4314eeeed394 | 4421:971a66627f7a |
---|---|
14 local add_task = require "util.timer".add_task; | 14 local add_task = require "util.timer".add_task; |
15 local socket = require "socket"; | 15 local socket = require "socket"; |
16 local format = string.format; | 16 local format = string.format; |
17 local t_insert, t_sort = table.insert, table.sort; | 17 local t_insert, t_sort = table.insert, table.sort; |
18 local get_traceback = debug.traceback; | 18 local get_traceback = debug.traceback; |
19 local tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber, setmetatable | 19 local tostring, pairs, ipairs, getmetatable, newproxy, next, error, tonumber, setmetatable |
20 = tostring, pairs, ipairs, getmetatable, newproxy, error, tonumber, setmetatable; | 20 = tostring, pairs, ipairs, getmetatable, newproxy, next, error, tonumber, setmetatable; |
21 | 21 |
22 local idna_to_ascii = require "util.encodings".idna.to_ascii; | 22 local idna_to_ascii = require "util.encodings".idna.to_ascii; |
23 local connlisteners_get = require "net.connlisteners".get; | 23 local connlisteners_get = require "net.connlisteners".get; |
24 local initialize_filters = require "util.filters".initialize; | 24 local initialize_filters = require "util.filters".initialize; |
25 local wrapclient = require "net.server".wrapclient; | 25 local wrapclient = require "net.server".wrapclient; |
26 local modulemanager = require "core.modulemanager"; | 26 local modulemanager = require "core.modulemanager"; |
27 local st = require "stanza"; | 27 local st = require "stanza"; |
28 local stanza = st.stanza; | 28 local stanza = st.stanza; |
29 local nameprep = require "util.encodings".stringprep.nameprep; | 29 local nameprep = require "util.encodings".stringprep.nameprep; |
30 local cert_verify_identity = require "util.x509".verify_identity; | 30 local cert_verify_identity = require "util.x509".verify_identity; |
31 local new_ip = require "util.ip".new_ip; | |
32 local rfc3484_dest = require "util.rfc3484".destination; | |
31 | 33 |
32 local fire_event = prosody.events.fire_event; | 34 local fire_event = prosody.events.fire_event; |
33 local uuid_gen = require "util.uuid".generate; | 35 local uuid_gen = require "util.uuid".generate; |
34 | 36 |
35 local logger_init = require "util.logger".init; | 37 local logger_init = require "util.logger".init; |
41 local adns, dns = require "net.adns", require "net.dns"; | 43 local adns, dns = require "net.adns", require "net.dns"; |
42 local config = require "core.configmanager"; | 44 local config = require "core.configmanager"; |
43 local connect_timeout = config.get("*", "core", "s2s_timeout") or 60; | 45 local connect_timeout = config.get("*", "core", "s2s_timeout") or 60; |
44 local dns_timeout = config.get("*", "core", "dns_timeout") or 15; | 46 local dns_timeout = config.get("*", "core", "dns_timeout") or 15; |
45 local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; | 47 local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; |
48 local sources; | |
46 | 49 |
47 dns.settimeout(dns_timeout); | 50 dns.settimeout(dns_timeout); |
48 | 51 |
49 local prosody = _G.prosody; | 52 local prosody = _G.prosody; |
50 incoming_s2s = {}; | 53 incoming_s2s = {}; |
263 end | 266 end |
264 end | 267 end |
265 end, "_xmpp-server._tcp."..connect_host..".", "SRV"); | 268 end, "_xmpp-server._tcp."..connect_host..".", "SRV"); |
266 | 269 |
267 return true; -- Attempt in progress | 270 return true; -- Attempt in progress |
271 elseif host_session.ip_hosts then | |
272 return try_connect(host_session, connect_host, connect_port, err); | |
268 elseif host_session.srv_hosts and #host_session.srv_hosts > host_session.srv_choice then -- Not our first attempt, and we also have SRV | 273 elseif host_session.srv_hosts and #host_session.srv_hosts > host_session.srv_choice then -- Not our first attempt, and we also have SRV |
269 host_session.srv_choice = host_session.srv_choice + 1; | 274 host_session.srv_choice = host_session.srv_choice + 1; |
270 local srv_choice = host_session.srv_hosts[host_session.srv_choice]; | 275 local srv_choice = host_session.srv_hosts[host_session.srv_choice]; |
271 connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port; | 276 connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port; |
272 host_session.log("info", "Connection failed (%s). Attempt #%d: This time to %s:%d", tostring(err), host_session.srv_choice, connect_host, connect_port); | 277 host_session.log("info", "Connection failed (%s). Attempt #%d: This time to %s:%d", tostring(err), host_session.srv_choice, connect_host, connect_port); |
283 end | 288 end |
284 | 289 |
285 return try_connect(host_session, connect_host, connect_port); | 290 return try_connect(host_session, connect_host, connect_port); |
286 end | 291 end |
287 | 292 |
288 function try_connect(host_session, connect_host, connect_port) | 293 function try_next_ip(host_session, connect_port) |
294 host_session.connecting = nil; | |
295 host_session.ip_choice = host_session.ip_choice + 1; | |
296 local ip = host_session.ip_hosts[host_session.ip_choice]; | |
297 local ok, err= make_connect(host_session, ip, connect_port); | |
298 if not ok then | |
299 if not attempt_connection(host_session, err or "closed") then | |
300 err = err and (": "..err) or ""; | |
301 destroy_session(host_session, "Connection failed"..err); | |
302 end | |
303 end | |
304 end | |
305 | |
306 function try_connect(host_session, connect_host, connect_port, err) | |
289 host_session.connecting = true; | 307 host_session.connecting = true; |
290 local handle; | 308 |
291 handle = adns.lookup(function (reply, err) | 309 if not err then |
292 handle = nil; | 310 local IPs = {}; |
293 host_session.connecting = nil; | 311 host_session.ip_hosts = IPs; |
294 | 312 local handle4, handle6; |
295 -- COMPAT: This is a compromise for all you CNAME-(ab)users :) | 313 local has_other = false; |
296 if not (reply and reply[#reply] and reply[#reply].a) then | 314 |
297 local count = max_dns_depth; | 315 if not sources then |
298 reply = dns.peek(connect_host, "CNAME", "IN"); | 316 sources = {}; |
299 while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do | 317 local cfg_sources = connlisteners_get("xmppserver").default_interface or config.get("*", "core", "interface"); |
300 log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); | 318 for i, source in ipairs(cfg_sources) do |
301 reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); | 319 if source == "*" then |
302 count = count - 1; | 320 sources[i] = new_ip("0.0.0.0", "IPv4"); |
303 end | 321 else |
304 end | 322 sources[i] = new_ip(source, (source:find(":") and "IPv6") or "IPv4"); |
305 -- end of CNAME resolving | 323 end |
306 | 324 end |
307 if reply and reply[#reply] and reply[#reply].a then | 325 end |
308 log("debug", "DNS reply for %s gives us %s", connect_host, reply[#reply].a); | 326 |
309 local ok, err = make_connect(host_session, reply[#reply].a, connect_port); | 327 handle4 = adns.lookup(function (reply, err) |
310 if not ok then | 328 handle4 = nil; |
311 if not attempt_connection(host_session, err or "closed") then | 329 |
312 err = err and (": "..err) or ""; | 330 -- COMPAT: This is a compromise for all you CNAME-(ab)users :) |
313 destroy_session(host_session, "Connection failed"..err); | 331 if not (reply and reply[#reply] and reply[#reply].a) then |
314 end | 332 local count = max_dns_depth; |
315 end | 333 reply = dns.peek(connect_host, "CNAME", "IN"); |
316 else | 334 while count > 0 and reply and reply[#reply] and not reply[#reply].a and reply[#reply].cname do |
317 log("debug", "DNS lookup failed to get a response for %s", connect_host); | 335 log("debug", "Looking up %s (DNS depth is %d)", tostring(reply[#reply].cname), count); |
318 if not attempt_connection(host_session, "name resolution failed") then -- Retry if we can | 336 reply = dns.peek(reply[#reply].cname, "A", "IN") or dns.peek(reply[#reply].cname, "CNAME", "IN"); |
319 log("debug", "No other records to try for %s - destroying", host_session.to_host); | 337 count = count - 1; |
320 err = err and (": "..err) or ""; | 338 end |
321 destroy_session(host_session, "DNS resolution failed"..err); -- End of the line, we can't | 339 end |
322 end | 340 -- end of CNAME resolving |
323 end | 341 |
324 end, connect_host, "A", "IN"); | 342 if reply and reply[#reply] and reply[#reply].a then |
343 for _, ip in ipairs(reply) do | |
344 log("debug", "DNS reply for %s gives us %s", connect_host, ip.a); | |
345 IPs[#IPs+1] = new_ip(ip.a, "IPv4"); | |
346 end | |
347 end | |
348 | |
349 if has_other then | |
350 rfc3484_dest(host_session.ip_hosts, sources); | |
351 host_session.ip_choice = 0; | |
352 try_next_ip(host_session, connect_port); | |
353 else | |
354 has_other = true; | |
355 end | |
356 end, connect_host, "A", "IN"); | |
357 | |
358 handle6 = adns.lookup(function (reply, err) | |
359 handle6 = nil; | |
360 | |
361 if reply and reply[#reply] and reply[#reply].aaaa then | |
362 for _, ip in ipairs(reply) do | |
363 log("debug", "DNS reply for %s gives us %s", connect_host, ip.aaaa); | |
364 IPs[#IPs+1] = new_ip(ip.aaaa, "IPv6"); | |
365 end | |
366 end | |
367 | |
368 if has_other then | |
369 rfc3484_dest(host_session.ip_hosts, sources); | |
370 host_session.ip_choice = 0; | |
371 try_next_ip(host_session, connect_port); | |
372 else | |
373 has_other = true; | |
374 end | |
375 end, connect_host, "AAAA", "IN"); | |
376 | |
377 return true; | |
378 elseif host_session.ip_hosts and #host_session.ip_hosts > host_session.ip_choice then -- Not our first attempt, and we also have IPs left to try | |
379 try_next_ip(host_session, connect_port); | |
380 else | |
381 host_session.ip_hosts = nil; | |
382 if not attempt_connection(host_session, "out of IP addresses") then -- Retry if we can | |
383 log("debug", "No other records to try for %s - destroying", host_session.to_host); | |
384 err = err and (": "..err) or ""; | |
385 destroy_session(host_session, "Connecting failed"..err); -- End of the line, we can't | |
386 return false; | |
387 end | |
388 end | |
325 | 389 |
326 return true; | 390 return true; |
327 end | 391 end |
328 | 392 |
329 function make_connect(host_session, connect_host, connect_port) | 393 function make_connect(host_session, connect_host, connect_port) |
330 (host_session.log or log)("info", "Beginning new connection attempt to %s (%s:%d)", host_session.to_host, connect_host, connect_port); | 394 (host_session.log or log)("info", "Beginning new connection attempt to %s ([%s]:%d)", host_session.to_host, connect_host.addr, connect_port); |
331 -- Ok, we're going to try to connect | 395 -- Ok, we're going to try to connect |
332 | 396 |
333 local from_host, to_host = host_session.from_host, host_session.to_host; | 397 local from_host, to_host = host_session.from_host, host_session.to_host; |
334 | 398 |
335 local conn, handler = socket.tcp(); | 399 local conn, handler; |
400 if connect_host.proto == "IPv4" then | |
401 conn, handler = socket.tcp(); | |
402 else | |
403 conn, handler = socket.tcp6(); | |
404 end | |
336 | 405 |
337 if not conn then | 406 if not conn then |
338 log("warn", "Failed to create outgoing connection, system error: %s", handler); | 407 log("warn", "Failed to create outgoing connection, system error: %s", handler); |
339 return false, handler; | 408 return false, handler; |
340 end | 409 end |
341 | 410 |
342 conn:settimeout(0); | 411 conn:settimeout(0); |
343 local success, err = conn:connect(connect_host, connect_port); | 412 local success, err = conn:connect(connect_host.addr, connect_port); |
344 if not success and err ~= "timeout" then | 413 if not success and err ~= "timeout" then |
345 log("warn", "s2s connect() to %s (%s:%d) failed: %s", host_session.to_host, connect_host, connect_port, err); | 414 log("warn", "s2s connect() to %s (%s:%d) failed: %s", host_session.to_host, connect_host.addr, connect_port, err); |
346 return false, err; | 415 return false, err; |
347 end | 416 end |
348 | 417 |
349 local cl = connlisteners_get("xmppserver"); | 418 local cl = connlisteners_get("xmppserver"); |
350 conn = wrapclient(conn, connect_host, connect_port, cl, cl.default_mode or 1 ); | 419 conn = wrapclient(conn, connect_host.addr, connect_port, cl, cl.default_mode or 1 ); |
351 host_session.conn = conn; | 420 host_session.conn = conn; |
352 | 421 |
353 local filter = initialize_filters(host_session); | 422 local filter = initialize_filters(host_session); |
354 local w, log = conn.write, host_session.log; | 423 local w, log = conn.write, host_session.log; |
355 host_session.sends2s = function (t) | 424 host_session.sends2s = function (t) |