diff options
Diffstat (limited to 'util')
-rw-r--r-- | util/bit53.lua | 2 | ||||
-rw-r--r-- | util/dependencies.lua | 2 | ||||
-rw-r--r-- | util/dns.lua | 2 | ||||
-rw-r--r-- | util/hex.lua | 6 | ||||
-rw-r--r-- | util/ip.lua | 2 | ||||
-rw-r--r-- | util/prosodyctl/check.lua | 165 | ||||
-rw-r--r-- | util/uuid.lua | 2 |
7 files changed, 174 insertions, 7 deletions
diff --git a/util/bit53.lua b/util/bit53.lua index 4b5c2e9c..06799a97 100644 --- a/util/bit53.lua +++ b/util/bit53.lua @@ -3,5 +3,7 @@ return { band = function (a, b) return a & b end; bor = function (a, b) return a | b end; bxor = function (a, b) return a ~ b end; + rshift = function (a, n) return a >> n end; + lshift = function (a, n) return a << n end; }; diff --git a/util/dependencies.lua b/util/dependencies.lua index 4cd45a05..d7836404 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -85,7 +85,7 @@ local function check_dependencies() { "Debian/Ubuntu", "sudo apt install lua-sec" }; { "luarocks", "luarocks install luasec" }; { "Source", "https://github.com/brunoos/luasec" }; - }, "SSL/TLS support will not be available", err); + }, nil, err); end local bit, err = softreq"util.bitcompat"; diff --git a/util/dns.lua b/util/dns.lua index 9d80d1ec..3b58e03e 100644 --- a/util/dns.lua +++ b/util/dns.lua @@ -13,7 +13,7 @@ local s_format = string.format; local s_sub = string.sub; local iana_data = require "util.dnsregistry"; -local tohex = require "util.hex".to; +local tohex = require "util.hex".encode; local inet_ntop = require "util.net".ntop; -- Simplified versions of Waqas DNS parsers diff --git a/util/hex.lua b/util/hex.lua index 4cc28d33..6202620f 100644 --- a/util/hex.lua +++ b/util/hex.lua @@ -23,4 +23,8 @@ local function from(s) return (s_gsub(s_lower(s), "%X*(%x%x)%X*", hex_to_char)); end -return { to = to, from = from } +return { + encode = to, decode = from; + -- COMPAT w/pre-0.12: + to = to, from = from; +}; diff --git a/util/ip.lua b/util/ip.lua index 20b92466..4b450934 100644 --- a/util/ip.lua +++ b/util/ip.lua @@ -67,7 +67,7 @@ function ip_methods:normal() end function ip_methods.bits(ip) - return hex.to(ip.packed):upper():gsub(".", hex2bits); + return hex.encode(ip.packed):upper():gsub(".", hex2bits); end function ip_methods.bits_full(ip) diff --git a/util/prosodyctl/check.lua b/util/prosodyctl/check.lua index bbae13fe..2ef3bbcb 100644 --- a/util/prosodyctl/check.lua +++ b/util/prosodyctl/check.lua @@ -60,6 +60,108 @@ local function check_probe(base_url, probe_module, target) return false, "Probe endpoint did not return a success status"; end +local function check_turn_service(turn_service) + local stun = require "net.stun"; + + -- Create UDP socket for communication with the server + local sock = assert(require "socket".udp()); + sock:setsockname("*", 0); + sock:setpeername(turn_service.host, turn_service.port); + sock:settimeout(10); + + -- Helper function to receive a packet + local function receive_packet() + local raw_packet, err = sock:receive(); + if not raw_packet then + return nil, err; + end + return stun.new_packet():deserialize(raw_packet); + end + + local result = { warnings = {} }; + + -- Send a "binding" query, i.e. a request for our external IP/port + local bind_query = stun.new_packet("binding", "request"); + bind_query:add_attribute("software", "prosodyctl check turn"); + sock:send(bind_query:serialize()); + + local bind_result, err = receive_packet(); + if not bind_result then + result.error = "No STUN response: "..err; + return result; + elseif bind_result:is_err_resp() then + result.error = ("STUN server returned error: %d (%s)"):format(bind_result:get_error()); + return result; + elseif not bind_result:is_success_resp() then + result.error = ("Unexpected STUN response: %d (%s)"):format(bind_result:get_type()); + return result; + end + + result.external_ip = bind_result:get_xor_mapped_address(); + if not result.external_ip then + result.error = "STUN server did not return an address"; + return result; + end + + -- Send a TURN "allocate" request. Expected to fail due to auth, but + -- necessary to obtain a valid realm/nonce from the server. + local pre_request = stun.new_packet("allocate", "request"); + sock:send(pre_request:serialize()); + + local pre_result, err = receive_packet(); + if not pre_result then + result.error = "No initial TURN response: "..err; + return result; + elseif pre_result:is_success_resp() then + result.error = "TURN server does not have authentication enabled"; + return result; + end + + local realm = pre_result:get_attribute("realm"); + local nonce = pre_result:get_attribute("nonce"); + + if not realm then + table.insert(result.warnings, "TURN server did not return an authentication realm"); + end + if not nonce then + table.insert(result.warnings, "TURN server did not return a nonce"); + end + + -- Use the configured secret to obtain temporary user/pass credentials + local turn_user, turn_pass = stun.get_user_pass_from_secret(turn_service.secret); + + -- Send a TURN allocate request, will fail if auth is wrong + local alloc_request = stun.new_packet("allocate", "request"); + alloc_request:add_requested_transport("udp"); + alloc_request:add_attribute("username", turn_user); + if realm then + alloc_request:add_attribute("realm", realm); + end + if nonce then + alloc_request:add_attribute("nonce", nonce); + end + local key = stun.get_long_term_auth_key(realm or turn_service.host, turn_user, turn_pass); + alloc_request:add_message_integrity(key); + sock:send(alloc_request:serialize()); + + -- Check the response + local alloc_response, err = receive_packet(); + if not alloc_response then + result.error = "TURN server did not response to allocation request: "..err; + return; + elseif alloc_response:is_err_resp() then + result.error = ("TURN allocation failed: %d (%s)"):format(alloc_response:get_error()); + return result; + elseif not alloc_response:is_success_resp() then + result.error = ("Unexpected TURN response: %d (%s)"):format(alloc_response:get_type()); + return result; + end + + -- No errors? Ok! + + return result; +end + local function skip_bare_jid_hosts(host) if jid_split(host) then -- See issue #779 @@ -80,8 +182,8 @@ local function check(arg) local ok = true; local function disabled_hosts(host, conf) return host ~= "*" and conf.enabled ~= false; end local function enabled_hosts() return it.filter(disabled_hosts, pairs(configmanager.getconfig())); end - if not (what == nil or what == "disabled" or what == "config" or what == "dns" or what == "certs" or what == "connectivity") then - show_warning("Don't know how to check '%s'. Try one of 'config', 'dns', 'certs', 'disabled' or 'connectivity'.", what); + if not (what == nil or what == "disabled" or what == "config" or what == "dns" or what == "certs" or what == "connectivity" or what == "turn") then + show_warning("Don't know how to check '%s'. Try one of 'config', 'dns', 'certs', 'disabled', 'turn' or 'connectivity'.", what); show_warning("Note: The connectivity check will connect to a remote server."); return 1; end @@ -1004,6 +1106,65 @@ local function check(arg) print("Note: The connectivity check only checks the reachability of the domain.") print("Note: It does not ensure that the check actually reaches this specific prosody instance.") end + + if what == "turn" then + local turn_enabled_hosts = {}; + local turn_services = {}; + + for host in enabled_hosts() do + local has_external_turn = modulemanager.get_modules_for_host(host):contains("turn_external"); + if has_external_turn then + table.insert(turn_enabled_hosts, host); + local turn_host = configmanager.get(host, "turn_external_host") or host; + local turn_port = configmanager.get(host, "turn_external_port") or 3478; + local turn_secret = configmanager.get(host, "turn_external_secret"); + if not turn_secret then + print("Error: Your configuration is missing a turn_external_secret for "..host); + print("Error: TURN will not be advertised for this host."); + ok = false; + else + local turn_id = ("%s:%d"):format(turn_host, turn_port); + if turn_services[turn_id] and turn_services[turn_id].secret ~= turn_secret then + print("Error: Your configuration contains multiple differing secrets"); + print(" for the TURN service at "..turn_id.." - we will only test one."); + elseif not turn_services[turn_id] then + turn_services[turn_id] = { + host = turn_host; + port = turn_port; + secret = turn_secret; + }; + end + end + end + end + + if what == "turn" then + local count = it.count(pairs(turn_services)); + if count == 0 then + print("Error: Unable to find any TURN services configured. Enable mod_turn_external!"); + else + print("Identified "..tostring(count).." TURN services."); + print(""); + end + end + + for turn_id, turn_service in pairs(turn_services) do + print("Testing "..turn_id.."..."); + + local result = check_turn_service(turn_service); + if #result.warnings > 0 then + print(("%d warnings:\n\n "):format(#result.warnings)); + print(table.concat(result.warnings, "\n ")); + end + if result.error then + print("Error: "..result.error.."\n"); + ok = false; + else + print("Success!\n"); + end + end + end + if not ok then print("Problems found, see above."); else diff --git a/util/uuid.lua b/util/uuid.lua index f4fd21f6..54ea99b4 100644 --- a/util/uuid.lua +++ b/util/uuid.lua @@ -8,7 +8,7 @@ local random = require "util.random"; local random_bytes = random.bytes; -local hex = require "util.hex".to; +local hex = require "util.hex".encode; local m_ceil = math.ceil; local function get_nibbles(n) |