namespace Xmpp { private class SrvTargetInfo { public string host { get; set; } public uint16 port { get; set; } public string service { get; set; } public uint16 priority { get; set; } } public class XmppStreamResult { public TlsXmppStream? stream { get; set; } public TlsCertificateFlags? tls_errors { get; set; } public IOError? io_error { get; set; } } public async XmppStreamResult establish_stream(Jid bare_jid, Gee.List modules, string? log_options, owned TlsXmppStream.OnInvalidCert on_invalid_cert) { Jid remote = bare_jid.domain_jid; TlsXmppStream.OnInvalidCertWrapper on_invalid_cert_wrapper = new TlsXmppStream.OnInvalidCertWrapper(on_invalid_cert); //Lookup xmpp-client and xmpps-client SRV records GLib.List? targets = new GLib.List(); GLibFixes.Resolver resolver = GLibFixes.Resolver.get_default(); try { GLib.List xmpp_services = yield resolver.lookup_service_async("xmpp-client", "tcp", remote.to_string(), null); foreach (SrvTarget service in xmpp_services) { targets.append(new SrvTargetInfo() { host=service.get_hostname(), port=service.get_port(), service="xmpp-client", priority=service.get_priority()}); } } catch (Error e) { debug("Got no xmpp-client DNS records for %s: %s", remote.to_string(), e.message); } try { GLib.List xmpp_services = yield resolver.lookup_service_async("xmpps-client", "tcp", remote.to_string(), null); foreach (SrvTarget service in xmpp_services) { targets.append(new SrvTargetInfo() { host=service.get_hostname(), port=service.get_port(), service="xmpps-client", priority=service.get_priority()}); } } catch (Error e) { debug("Got no xmpps-client DNS records for %s: %s", remote.to_string(), e.message); } targets.sort((a, b) => { return a.priority - b.priority; }); // Add fallback connection bool should_add_fallback = true; foreach (SrvTargetInfo target in targets) { if (target.service == "xmpp-client" && target.port == 5222 && target.host == remote.to_string()) { should_add_fallback = false; } } if (should_add_fallback) { targets.append(new SrvTargetInfo() { host=remote.to_string(), port=5222, service="xmpp-client", priority=uint16.MAX}); } // Try all connection options from lowest to highest priority TlsXmppStream? stream = null; TlsCertificateFlags? tls_errors = null; IOError? io_error = null; foreach (SrvTargetInfo target in targets) { try { if (target.service == "xmpp-client") { stream = new StartTlsXmppStream(remote, target.host, target.port, on_invalid_cert_wrapper); } else { stream = new DirectTlsXmppStream(remote, target.host, target.port, on_invalid_cert_wrapper); } stream.log = new XmppLog(bare_jid.to_string(), log_options); foreach (XmppStreamModule module in modules) { stream.add_module(module); } uint connection_timeout_id = Timeout.add_seconds(30, () => { warning("Connection attempt timed out"); stream.disconnect(); return Source.REMOVE; }); yield stream.connect(); Source.remove(connection_timeout_id); return new XmppStreamResult() { stream=stream }; } catch (IOError e) { warning("Could not establish XMPP session with %s:%i: %s", target.host, target.port, e.message); if (stream != null) { if (stream.errors != null) { tls_errors = stream.errors; } io_error = e; stream.detach_modules(); } } } return new XmppStreamResult() { io_error=io_error, tls_errors=tls_errors }; } }