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)