6 local t_insert, t_sort = table.insert, table.sort; |
6 local t_insert, t_sort = table.insert, table.sort; |
7 local get_traceback = debug.traceback; |
7 local get_traceback = debug.traceback; |
8 local tostring, pairs, ipairs, getmetatable, print, newproxy, error, tonumber |
8 local tostring, pairs, ipairs, getmetatable, print, newproxy, error, tonumber |
9 = tostring, pairs, ipairs, getmetatable, print, newproxy, error, tonumber; |
9 = tostring, pairs, ipairs, getmetatable, print, newproxy, error, tonumber; |
10 |
10 |
|
11 local idna_to_ascii = require "util.encodings".idna.to_ascii; |
11 local connlisteners_get = require "net.connlisteners".get; |
12 local connlisteners_get = require "net.connlisteners".get; |
12 local wraptlsclient = require "net.server".wraptlsclient; |
13 local wraptlsclient = require "net.server".wraptlsclient; |
13 local modulemanager = require "core.modulemanager"; |
14 local modulemanager = require "core.modulemanager"; |
14 local st = require "stanza"; |
15 local st = require "stanza"; |
15 local stanza = st.stanza; |
16 local stanza = st.stanza; |
82 end |
83 end |
83 |
84 |
84 function new_outgoing(from_host, to_host) |
85 function new_outgoing(from_host, to_host) |
85 local host_session = { to_host = to_host, from_host = from_host, notopen = true, type = "s2sout_unauthed", direction = "outgoing" }; |
86 local host_session = { to_host = to_host, from_host = from_host, notopen = true, type = "s2sout_unauthed", direction = "outgoing" }; |
86 hosts[from_host].s2sout[to_host] = host_session; |
87 hosts[from_host].s2sout[to_host] = host_session; |
87 local cl = connlisteners_get("xmppserver"); |
88 |
88 |
89 local log; |
89 local conn, handler = socket.tcp() |
90 do |
90 |
91 local conn_name = "s2sout"..tostring(conn):match("[a-f0-9]*$"); |
91 local connect_host, connect_port = to_host, 5269; |
92 log = logger_init(conn_name); |
92 |
93 host_session.log = log; |
93 local answer = dns.lookup("_xmpp-server._tcp."..to_host..".", "SRV"); |
94 end |
|
95 |
|
96 attempt_connection(host_session); |
|
97 |
|
98 return host_session; |
|
99 end |
|
100 |
|
101 |
|
102 function attempt_connection(host_session, err) |
|
103 local from_host, to_host = host_session.from_host, host_session.to_host; |
|
104 local conn, handler = socket.tcp() |
|
105 |
|
106 local connect_host, connect_port = idna_to_ascii(to_host), 5269; |
|
107 |
|
108 if not err then -- This is our first attempt |
|
109 local answer = dns.lookup("_xmpp-server._tcp."..connect_host..".", "SRV"); |
94 |
110 |
95 if answer then |
111 if answer then |
96 log("debug", to_host.." has SRV records, handling..."); |
112 log("debug", to_host.." has SRV records, handling..."); |
97 local srv_hosts = {}; |
113 local srv_hosts = {}; |
98 host_session.srv_hosts = srv_hosts; |
114 host_session.srv_hosts = srv_hosts; |
100 t_insert(srv_hosts, record.srv); |
116 t_insert(srv_hosts, record.srv); |
101 end |
117 end |
102 t_sort(srv_hosts, compare_srv_priorities); |
118 t_sort(srv_hosts, compare_srv_priorities); |
103 |
119 |
104 local srv_choice = srv_hosts[1]; |
120 local srv_choice = srv_hosts[1]; |
|
121 host_session.srv_choice = 1; |
105 if srv_choice then |
122 if srv_choice then |
106 connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port; |
123 connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port; |
107 log("debug", "Best record found, will connect to %s:%d", connect_host, connect_port); |
124 log("debug", "Best record found, will connect to %s:%d", connect_host, connect_port); |
108 end |
125 end |
109 end |
126 end |
110 |
127 elseif host_session.srv_hosts and #host_session.srv_hosts > host_session.srv_choice then -- Not our first attempt, and we also have SRV |
111 conn:settimeout(0); |
128 host_session.srv_choice = host_session.srv_choice + 1; |
112 local success, err = conn:connect(connect_host, connect_port); |
129 local srv_choice = host_session.srv_hosts[host_session.srv_choice]; |
113 if not success and err ~= "timeout" then |
130 connect_host, connect_port = srv_choice.target or to_host, srv_choice.port or connect_port; |
114 log("warn", "s2s connect() failed: %s", err); |
131 host_session.log("debug", "Attempt #%d: This time to %s:%d", host_session.srv_choice, connect_host, connect_port); |
115 end |
132 else |
116 |
133 host_session.log("debug", "Out of connection options, can't connect to %s", tostring(host_session.to_host)); |
117 conn = wraptlsclient(cl, conn, connect_host, connect_port, 0, 1, hosts[from_host].ssl_ctx ); |
134 -- We're out of options |
118 host_session.conn = conn; |
135 return false; |
119 |
136 end |
120 -- Register this outgoing connection so that xmppserver_listener knows about it |
137 |
121 -- otherwise it will assume it is a new incoming connection |
138 -- Ok, we're going to try to connect |
122 cl.register_outgoing(conn, host_session); |
139 conn:settimeout(0); |
123 |
140 local success, err = conn:connect(connect_host, connect_port); |
124 local log; |
141 if not success and err ~= "timeout" then |
125 do |
142 log("warn", "s2s connect() failed: %s", err); |
126 local conn_name = "s2sout"..tostring(conn):match("[a-f0-9]*$"); |
143 end |
127 log = logger_init(conn_name); |
144 |
128 host_session.log = log; |
145 local cl = connlisteners_get("xmppserver"); |
129 end |
146 conn = wraptlsclient(cl, conn, connect_host, connect_port, 0, 1, hosts[from_host].ssl_ctx ); |
130 |
147 host_session.conn = conn; |
131 local w = conn.write; |
148 |
132 host_session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end |
149 -- Register this outgoing connection so that xmppserver_listener knows about it |
133 |
150 -- otherwise it will assume it is a new incoming connection |
134 conn.write(format([[<stream:stream xmlns='jabber:server' xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' from='%s' to='%s' version='1.0'>]], from_host, to_host)); |
151 cl.register_outgoing(conn, host_session); |
135 |
152 |
136 return host_session; |
153 local w = conn.write; |
|
154 host_session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end |
|
155 |
|
156 conn.write(format([[<stream:stream xmlns='jabber:server' xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' from='%s' to='%s' version='1.0'>]], from_host, to_host)); |
|
157 return true; |
137 end |
158 end |
138 |
159 |
139 function streamopened(session, attr) |
160 function streamopened(session, attr) |
140 local send = session.sends2s; |
161 local send = session.sends2s; |
141 |
162 |
157 (session.log or log)("debug", "incoming s2s received <stream:stream>"); |
178 (session.log or log)("debug", "incoming s2s received <stream:stream>"); |
158 send("<?xml version='1.0'?>"); |
179 send("<?xml version='1.0'?>"); |
159 send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host }):top_tag()); |
180 send(stanza("stream:stream", { xmlns='jabber:server', ["xmlns:db"]='jabber:server:dialback', ["xmlns:stream"]='http://etherx.jabber.org/streams', id=session.streamid, from=session.to_host }):top_tag()); |
160 if session.to_host and not hosts[session.to_host] then |
181 if session.to_host and not hosts[session.to_host] then |
161 -- Attempting to connect to a host we don't serve |
182 -- Attempting to connect to a host we don't serve |
162 session:close("host-unknown"); |
183 session:close({ condition = "host-unknown"; text = "This host does not serve "..session.to_host }); |
163 return; |
184 return; |
164 end |
185 end |
165 if session.version >= 1.0 then |
186 if session.version >= 1.0 then |
166 send(st.stanza("stream:features") |
187 send(st.stanza("stream:features") |
167 :tag("dialback", { xmlns='urn:xmpp:features:dialback' }):tag("optional"):up():up()); |
188 :tag("dialback", { xmlns='urn:xmpp:features:dialback' }):tag("optional"):up():up()); |