X-Git-Url: https://git.enpas.org/?a=blobdiff_plain;f=plugins%2Fmod_admin_telnet.lua;h=b2a013247c7026fa60dbdf6b46e215df9aa6fd97;hb=db2ebc9585c3d52cd30f913728fedb54d76298f2;hp=e13d27c24a9ac2c8019e26ad6956aa8854c63a47;hpb=a2fc95c857560f2c3221afcb0967f82fbbf3ce93;p=prosody.git diff --git a/plugins/mod_admin_telnet.lua b/plugins/mod_admin_telnet.lua index e13d27c2..b2a01324 100644 --- a/plugins/mod_admin_telnet.lua +++ b/plugins/mod_admin_telnet.lua @@ -22,12 +22,12 @@ local console_listener = { default_port = 5582; default_mode = "*a"; interface = local iterators = require "util.iterators"; local keys, values = iterators.keys, iterators.values; -local jid = require "util.jid"; -local jid_bare, jid_split = jid.bare, jid.split; +local jid_bare, jid_split, jid_join = import("util.jid", "bare", "prepped_split", "join"); local set, array = require "util.set", require "util.array"; local cert_verify_identity = require "util.x509".verify_identity; local envload = require "util.envload".envload; local envloadfile = require "util.envload".envloadfile; +local has_pposix, pposix = pcall(require, "util.pposix"); local commands = module:shared("commands") local def_env = module:shared("env"); @@ -155,6 +155,14 @@ function console_listener.onincoming(conn, data) session.partial_data = data:match("[^\n]+$"); end +function console_listener.onreadtimeout(conn) + local session = sessions[conn]; + if session then + session.send("\0"); + return true; + end +end + function console_listener.ondisconnect(conn, err) local session = sessions[conn]; if session then @@ -163,6 +171,10 @@ function console_listener.ondisconnect(conn, err) end end +function console_listener.ondetach(conn) + sessions[conn] = nil; +end + -- Console commands -- -- These are simple commands, not valid standalone in Lua @@ -213,9 +225,11 @@ function commands.help(session, data) print [[c2s:show(jid) - Show all client sessions with the specified JID (or all if no JID given)]] print [[c2s:show_insecure() - Show all unencrypted client connections]] print [[c2s:show_secure() - Show all encrypted client connections]] + print [[c2s:show_tls() - Show TLS cipher info for encrypted sessions]] print [[c2s:close(jid) - Close all sessions for the specified JID]] elseif section == "s2s" then print [[s2s:show(domain) - Show all s2s connections for the given domain (or all if no domain given)]] + print [[s2s:show_tls(domain) - Show TLS cipher info for encrypted sessions]] print [[s2s:close(from, to) - Close a connection from one domain to another]] print [[s2s:closeall(host) - Close all the incoming/outgoing s2s sessions to specified host]] elseif section == "module" then @@ -268,6 +282,8 @@ end -- Session environment -- -- Anything in def_env will be accessible within the session as a global variable +--luacheck: ignore 212/self + def_env.server = {}; function def_env.server:insane_reload() @@ -309,7 +325,7 @@ local function human(kb) end function def_env.server:memory() - if not pposix.meminfo then + if not has_pposix or not pposix.meminfo then return true, "Lua is using "..collectgarbage("count"); end local mem, lua_mem = pposix.meminfo(), collectgarbage("count"); @@ -332,10 +348,9 @@ local function get_hosts_set(hosts, module) elseif type(hosts) == "string" then return set.new { hosts }; elseif hosts == nil then - local mm = require "modulemanager"; local hosts_set = set.new(array.collect(keys(prosody.hosts))) - / function (host) return (prosody.hosts[host].type == "local" or module and mm.is_loaded(host, module)) and host or nil; end; - if module and mm.get_module("*", module) then + / function (host) return (prosody.hosts[host].type == "local" or module and modulemanager.is_loaded(host, module)) and host or nil; end; + if module and modulemanager.get_module("*", module) then hosts_set:add("*"); end return hosts_set; @@ -343,15 +358,13 @@ local function get_hosts_set(hosts, module) end function def_env.module:load(name, hosts, config) - local mm = require "modulemanager"; - hosts = get_hosts_set(hosts); -- Load the module for each host local ok, err, count, mod = true, nil, 0, nil; for host in hosts do - if (not mm.is_loaded(host, name)) then - mod, err = mm.load(host, name, config); + if (not modulemanager.is_loaded(host, name)) then + mod, err = modulemanager.load(host, name, config); if not mod then ok = false; if err == "global-module-already-loaded" then @@ -372,15 +385,13 @@ function def_env.module:load(name, hosts, config) end function def_env.module:unload(name, hosts) - local mm = require "modulemanager"; - hosts = get_hosts_set(hosts, name); -- Unload the module for each host local ok, err, count = true, nil, 0; for host in hosts do - if mm.is_loaded(host, name) then - ok, err = mm.unload(host, name); + if modulemanager.is_loaded(host, name) then + ok, err = modulemanager.unload(host, name); if not ok then ok = false; self.session.print(err or "Unknown error unloading module"); @@ -394,8 +405,6 @@ function def_env.module:unload(name, hosts) end function def_env.module:reload(name, hosts) - local mm = require "modulemanager"; - hosts = array.collect(get_hosts_set(hosts, name)):sort(function (a, b) if a == "*" then return true elseif b == "*" then return false @@ -405,8 +414,8 @@ function def_env.module:reload(name, hosts) -- Reload the module for each host local ok, err, count = true, nil, 0; for _, host in ipairs(hosts) do - if mm.is_loaded(host, name) then - ok, err = mm.reload(host, name); + if modulemanager.is_loaded(host, name) then + ok, err = modulemanager.reload(host, name); if not ok then ok = false; self.session.print(err or "Unknown error reloading module"); @@ -472,22 +481,28 @@ function def_env.config:reload() return ok, (ok and "Config reloaded (you may need to reload modules to take effect)") or tostring(err); end -def_env.hosts = {}; -function def_env.hosts:list() - for host, host_session in pairs(hosts) do - self.session.print(host); +local function common_info(session, line) + if session.id then + line[#line+1] = "["..session.id.."]" + else + line[#line+1] = "["..session.type..(tostring(session):match("%x*$")).."]" end - return true, "Done"; -end - -function def_env.hosts:add(name) end local function session_flags(session, line) line = line or {}; + common_info(session, line); + if session.type == "c2s" then + local status, priority = "unavailable", tostring(session.priority or "-"); + if session.presence then + status = session.presence:get_child_text("show") or "available"; + end + line[#line+1] = status.."("..priority..")"; + end if session.cert_identity_status == "valid" then - line[#line+1] = "(secure)"; - elseif session.secure then + line[#line+1] = "(authenticated)"; + end + if session.secure then line[#line+1] = "(encrypted)"; end if session.compressed then @@ -499,47 +514,75 @@ local function session_flags(session, line) if session.ip and session.ip:match(":") then line[#line+1] = "(IPv6)"; end + if session.remote then + line[#line+1] = "(remote)"; + end + return table.concat(line, " "); +end + +local function tls_info(session, line) + line = line or {}; + common_info(session, line); + if session.secure then + local sock = session.conn and session.conn.socket and session.conn:socket(); + if sock and sock.info then + local info = sock:info(); + line[#line+1] = ("(%s with %s)"):format(info.protocol, info.cipher); + else + line[#line+1] = "(cipher info unavailable)"; + end + else + line[#line+1] = "(insecure)"; + end return table.concat(line, " "); end def_env.c2s = {}; +local function get_jid(session) + if session.username then + return session.full_jid or jid_join(session.username, session.host, session.resource); + end + + local conn = session.conn; + local ip = session.ip or "?"; + local clientport = conn and conn:clientport() or "?"; + local serverip = conn and conn.server and conn:server():ip() or "?"; + local serverport = conn and conn:serverport() or "?" + return jid_join("["..ip.."]:"..clientport, session.host or "["..serverip.."]:"..serverport); +end + local function show_c2s(callback) - for hostname, host in pairs(hosts) do - for username, user in pairs(host.sessions or {}) do - for resource, session in pairs(user.sessions or {}) do - local jid = username.."@"..hostname.."/"..resource; - callback(jid, session); + local c2s = array.collect(values(module:shared"/*/c2s/sessions")); + c2s:sort(function(a, b) + if a.host == b.host then + if a.username == b.username then + return (a.resource or "") > (b.resource or ""); end + return (a.username or "") > (b.username or ""); end - end + return (a.host or "") > (b.host or ""); + end):map(function (session) + callback(get_jid(session), session) + end); end function def_env.c2s:count(match_jid) - local count = 0; - show_c2s(function (jid, session) - if (not match_jid) or jid:match(match_jid) then - count = count + 1; - end - end); - return true, "Total: "..count.." clients"; + return true, "Total: ".. iterators.count(values(module:shared"/*/c2s/sessions")) .." clients"; end -function def_env.c2s:show(match_jid) +function def_env.c2s:show(match_jid, annotate) local print, count = self.session.print, 0; - local curr_host; + annotate = annotate or session_flags; + local curr_host = false; show_c2s(function (jid, session) if curr_host ~= session.host then curr_host = session.host; - print(curr_host); + print(curr_host or "(not connected to any host yet)"); end if (not match_jid) or jid:match(match_jid) then count = count + 1; - local status, priority = "unavailable", tostring(session.priority or "-"); - if session.presence then - status = session.presence:get_child_text("show") or "available"; - end - print(session_flags(session, { " "..jid.." - "..status.."("..priority..")" })); + print(annotate(session, { " ", jid })); end end); return true, "Total: "..count.." clients"; @@ -567,6 +610,10 @@ function def_env.c2s:show_secure(match_jid) return true, "Total: "..count.." secure client connections"; end +function def_env.c2s:show_tls(match_jid) + return self:show(match_jid, tls_info); +end + function def_env.c2s:close(match_jid) local count = 0; show_c2s(function (jid, session) @@ -580,8 +627,9 @@ end def_env.s2s = {}; -function def_env.s2s:show(match_jid) +function def_env.s2s:show(match_jid, annotate) local print = self.session.print; + annotate = annotate or session_flags; local count_in, count_out = 0,0; local s2s_list = { }; @@ -599,8 +647,7 @@ function def_env.s2s:show(match_jid) remotehost, localhost = session.from_host or "?", session.to_host or "?"; end local sess_lines = { l = localhost, r = remotehost, - session_flags(session, { "", direction, remotehost or "?", - "["..session.type..tostring(session):match("[a-f0-9]*$").."]" })}; + annotate(session, { "", direction, remotehost or "?" })}; if (not match_jid) or remotehost:match(match_jid) or localhost:match(match_jid) then table.insert(s2s_list, sess_lines); @@ -655,6 +702,10 @@ function def_env.s2s:show(match_jid) return true, "Total: "..count_out.." outgoing, "..count_in.." incoming connections"; end +function def_env.s2s:show_tls(match_jid) + return self:show(match_jid, tls_info); +end + local function print_subject(print, subject) for _, entry in ipairs(subject) do print( @@ -795,19 +846,19 @@ function def_env.s2s:close(from, to) (session.close or s2smanager.destroy_session)(session); count = count + 1 ; end - end + end return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end function def_env.s2s:closeall(host) - local count = 0; + local count = 0; local s2s_sessions = module:shared"/*/s2s/sessions"; for _,session in pairs(s2s_sessions) do if not host or session.from_host == host or session.to_host == host then session:close(); - count = count + 1; - end - end + count = count + 1; + end + end if count == 0 then return false, "No sessions to close."; else return true, "Closed "..count.." s2s session"..((count == 1 and "") or "s"); end end @@ -824,9 +875,19 @@ end function def_env.host:list() local print = self.session.print; local i = 0; + local type; for host in values(array.collect(keys(prosody.hosts)):sort()) do i = i + 1; - print(host); + type = hosts[host].type; + if type == "local" then + print(host); + else + type = module:context(host):get_option_string("component_module", type); + if type ~= "component" then + type = type .. " component"; + end + print(("%s (%s)"):format(host, type)); + end end return true, i.." hosts"; end @@ -896,14 +957,20 @@ local function check_muc(jid) end function def_env.muc:create(room_jid) - local room, host = check_muc(room_jid); - if not room then return nil, host end + local room_name, host = check_muc(room_jid); + if not room_name then + return room_name, host; + end + if not room_name then return nil, host end if hosts[host].modules.muc.rooms[room_jid] then return nil, "Room exists already" end return hosts[host].modules.muc.create_room(room_jid); end function def_env.muc:room(room_jid) local room_name, host = check_muc(room_jid); + if not room_name then + return room_name, host; + end local room_obj = hosts[host].modules.muc.rooms[room_jid]; if not room_obj then return nil, "No such room: "..room_jid; @@ -911,6 +978,20 @@ function def_env.muc:room(room_jid) return setmetatable({ room = room_obj }, console_room_mt); end +function def_env.muc:list(host) + local host_session = hosts[host]; + if not host_session or not host_session.modules.muc then + return nil, "Please supply the address of a local MUC component"; + end + local print = self.session.print; + local c = 0; + for name in keys(host_session.modules.muc.rooms) do + print(name); + c = c + 1; + end + return true, c.." rooms"; +end + local um = require"core.usermanager"; def_env.user = {}; @@ -1007,12 +1088,12 @@ function def_env.dns:lookup(name, typ, class) end function def_env.dns:addnameserver(...) - dns.addnameserver(...) + dns._resolver:addnameserver(...) return true end function def_env.dns:setnameserver(...) - dns.setnameserver(...) + dns._resolver:setnameserver(...) return true end @@ -1025,11 +1106,38 @@ function def_env.dns:cache() return true, "Cache:\n"..tostring(dns.cache()) end +def_env.http = {}; + +function def_env.http:list() + local print = self.session.print; + + for host in pairs(prosody.hosts) do + local http_apps = modulemanager.get_items("http-provider", host); + if #http_apps > 0 then + local http_host = module:context(host):get_option("http_host"); + print("HTTP endpoints on "..host..(http_host and (" (using "..http_host.."):") or ":")); + for _, provider in ipairs(http_apps) do + local url = module:context(host):http_url(provider.name); + print("", url); + end + print(""); + end + end + + local default_host = module:get_option("http_default_host"); + if not default_host then + print("HTTP requests to unknown hosts will return 404 Not Found"); + else + print("HTTP requests to unknown hosts will be handled by "..default_host); + end + return true; +end + ------------- function printbanner(session) - local option = module:get_option("console_banner"); - if option == nil or option == "full" or option == "graphic" then + local option = module:get_option_string("console_banner", "full"); + if option == "full" or option == "graphic" then session.print [[ ____ \ / _ | _ \ _ __ ___ ___ _-_ __| |_ _ @@ -1040,17 +1148,13 @@ function printbanner(session) ]] end - if option == nil or option == "short" or option == "full" then + if option == "short" or option == "full" then session.print("Welcome to the Prosody administration console. For a list of commands, type: help"); session.print("You may find more help on using this console in our online documentation at "); session.print("http://prosody.im/doc/console\n"); end - if option and option ~= "short" and option ~= "full" and option ~= "graphic" then - if type(option) == "string" then - session.print(option) - elseif type(option) == "function" then - module:log("warn", "Using functions as value for the console_banner option is no longer supported"); - end + if option ~= "short" and option ~= "full" and option ~= "graphic" then + session.print(option); end end