From 2151270b0c03e5cdda214c6199468e35234e4455 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Mon, 10 Aug 2009 12:14:40 +0200 Subject: Initial commit of the SASL redesign. --- util/sasl.lua | 251 ++++++---------------------------------------------------- 1 file changed, 23 insertions(+), 228 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index 48412ea7..b2dd4034 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -27,244 +27,39 @@ local math = require "math" local type = type local error = error local print = print +local setmetatable = setmetatable; +local assert = assert; module "sasl" --- Credentials handler: --- Arguments: ("PLAIN", user, host, password) --- Returns: true (success) | false (fail) | nil (user unknown) -local function new_plain(realm, credentials_handler) - local object = { mechanism = "PLAIN", realm = realm, credentials_handler = credentials_handler} - function object.feed(self, message) - if message == "" or message == nil then return "failure", "malformed-request" end - local response = message - local authorization = s_match(response, "([^&%z]+)") - local authentication = s_match(response, "%z([^&%z]+)%z") - local password = s_match(response, "%z[^&%z]+%z([^&%z]+)") - - if authentication == nil or password == nil then return "failure", "malformed-request" end - self.username = authentication - local auth_success = self.credentials_handler("PLAIN", self.username, self.realm, password) - - if auth_success then - return "success" - elseif auth_success == nil then - return "failure", "account-disabled" - else - return "failure", "not-authorized" - end - end - return object +local method = {} +local mechanisms = {}; +local backend_mechanism = {}; + +-- register a new SASL mechanims +local function registerMechanism(name, backends, f) + assert(type(name) == "string", "Parameter name MUST be a string."); + assert(type(backends) == "string" or type(backends) == "table", "Parameter backends MUST be either a string or a table."); + assert(type(f) == "function", "Parameter f MUST be a function."); + mechanism[name] = f + for _, backend_name in ipairs(backend) end --- credentials_handler: --- Arguments: (mechanism, node, domain, realm, decoder) --- Returns: Password encoding, (plaintext) password --- implementing RFC 2831 -local function new_digest_md5(realm, credentials_handler) - --TODO complete support for authzid - - local function serialize(message) - local data = "" - - if type(message) ~= "table" then error("serialize needs an argument of type table.") end - - -- testing all possible values - if message["nonce"] then data = data..[[nonce="]]..message.nonce..[[",]] end - if message["qop"] then data = data..[[qop="]]..message.qop..[[",]] end - if message["charset"] then data = data..[[charset=]]..message.charset.."," end - if message["algorithm"] then data = data..[[algorithm=]]..message.algorithm.."," end - if message["realm"] then data = data..[[realm="]]..message.realm..[[",]] end - if message["rspauth"] then data = data..[[rspauth=]]..message.rspauth.."," end - data = data:gsub(",$", "") - return data - end - - local function utf8tolatin1ifpossible(passwd) - local i = 1; - while i <= #passwd do - local passwd_i = to_byte(passwd:sub(i, i)); - if passwd_i > 0x7F then - if passwd_i < 0xC0 or passwd_i > 0xC3 then - return passwd; - end - i = i + 1; - passwd_i = to_byte(passwd:sub(i, i)); - if passwd_i < 0x80 or passwd_i > 0xBF then - return passwd; - end - end - i = i + 1; - end - - local p = {}; - local j = 0; - i = 1; - while (i <= #passwd) do - local passwd_i = to_byte(passwd:sub(i, i)); - if passwd_i > 0x7F then - i = i + 1; - local passwd_i_1 = to_byte(passwd:sub(i, i)); - t_insert(p, to_char(passwd_i%4*64 + passwd_i_1%64)); -- I'm so clever - else - t_insert(p, to_char(passwd_i)); - end - i = i + 1; - end - return t_concat(p); - end - local function latin1toutf8(str) - local p = {}; - for ch in gmatch(str, ".") do - ch = to_byte(ch); - if (ch < 0x80) then - t_insert(p, to_char(ch)); - elseif (ch < 0xC0) then - t_insert(p, to_char(0xC2, ch)); - else - t_insert(p, to_char(0xC3, ch - 64)); - end - end - return t_concat(p); - end - local function parse(data) - message = {} - for k, v in gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder - message[k] = v; - end - return message; - end - - local object = { mechanism = "DIGEST-MD5", realm = realm, credentials_handler = credentials_handler}; - - object.nonce = generate_uuid(); - object.step = 0; - object.nonce_count = {}; - - function object.feed(self, message) - self.step = self.step + 1; - if (self.step == 1) then - local challenge = serialize({ nonce = object.nonce, - qop = "auth", - charset = "utf-8", - algorithm = "md5-sess", - realm = self.realm}); - return "challenge", challenge; - elseif (self.step == 2) then - local response = parse(message); - -- check for replay attack - if response["nc"] then - if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end - end - - -- check for username, it's REQUIRED by RFC 2831 - if not response["username"] then - return "failure", "malformed-request"; - end - self["username"] = response["username"]; - - -- check for nonce, ... - if not response["nonce"] then - return "failure", "malformed-request"; - else - -- check if it's the right nonce - if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end - end - - if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end - if not response["qop"] then response["qop"] = "auth" end - - if response["realm"] == nil or response["realm"] == "" then - response["realm"] = ""; - elseif response["realm"] ~= self.realm then - return "failure", "not-authorized", "Incorrect realm value"; - end - - local decoder; - if response["charset"] == nil then - decoder = utf8tolatin1ifpossible; - elseif response["charset"] ~= "utf-8" then - return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; - end - - local domain = ""; - local protocol = ""; - if response["digest-uri"] then - protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); - if protocol == nil or domain == nil then return "failure", "malformed-request" end - else - return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." - end - - --TODO maybe realm support - self.username = response["username"]; - local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], to_unicode(domain), response["realm"], decoder); - if Y == nil then return "failure", "not-authorized" - elseif Y == false then return "failure", "account-disabled" end - local A1 = ""; - if response.authzid then - if response.authzid == self.username.."@"..self.realm then - -- COMPAT - log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; - else - A1 = "?"; - end - else - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; - end - local A2 = "AUTHENTICATE:"..protocol.."/"..domain; - - local HA1 = md5(A1, true); - local HA2 = md5(A2, true); - - local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; - local response_value = md5(KD, true); - - if response_value == response["response"] then - -- calculate rspauth - A2 = ":"..protocol.."/"..domain; - - HA1 = md5(A1, true); - HA2 = md5(A2, true); - - KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 - local rspauth = md5(KD, true); - self.authenticated = true; - return "challenge", serialize({rspauth = rspauth}); - else - return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." - end - elseif self.step == 3 then - if self.authenticated ~= nil then return "success" - else return "failure", "malformed-request" end - end - end - return object; +-- create a new SASL object which can be used to authenticate clients +function new(realm, profile) + sasl_i = {}; + + return setmetatable(sasl_i, method); end --- Credentials handler: Can be nil. If specified, should take the mechanism as --- the only argument, and return true for OK, or false for not-OK (TODO) -local function new_anonymous(realm, credentials_handler) - local object = { mechanism = "ANONYMOUS", realm = realm, credentials_handler = credentials_handler} - function object.feed(self, message) - return "success" - end - object["username"] = generate_uuid() - return object +-- get a list of possible SASL mechanims to use +function method:mechanisms() + end +-- select a mechanism to use +function method.select( mechanism ) -function new(mechanism, realm, credentials_handler) - local object - if mechanism == "PLAIN" then object = new_plain(realm, credentials_handler) - elseif mechanism == "DIGEST-MD5" then object = new_digest_md5(realm, credentials_handler) - elseif mechanism == "ANONYMOUS" then object = new_anonymous(realm, credentials_handler) - else - log("debug", "Unsupported SASL mechanism: "..tostring(mechanism)); - return nil - end - return object end return _M; -- cgit v1.2.3 From e8d31c23939c42c0489f33fba40a13bbecb966e4 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Mon, 10 Aug 2009 23:04:19 +0200 Subject: Mostly making the code run; includes fixing typos and so on. --- util/sasl.lua | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index b2dd4034..d6ac5c1e 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -17,6 +17,7 @@ local log = require "util.logger".init("sasl"); local tostring = tostring; local st = require "util.stanza"; local generate_uuid = require "util.uuid".generate; +local pairs, ipairs = pairs, ipairs; local t_insert, t_concat = table.insert, table.concat; local to_byte, to_char = string.byte, string.char; local to_unicode = require "util.encodings".idna.to_unicode; @@ -30,9 +31,14 @@ local print = print local setmetatable = setmetatable; local assert = assert; +require "util.iterators" +local keys = keys + +local array = require "util.array" module "sasl" -local method = {} +local method = {}; +method.__index = method; local mechanisms = {}; local backend_mechanism = {}; @@ -41,25 +47,68 @@ local function registerMechanism(name, backends, f) assert(type(name) == "string", "Parameter name MUST be a string."); assert(type(backends) == "string" or type(backends) == "table", "Parameter backends MUST be either a string or a table."); assert(type(f) == "function", "Parameter f MUST be a function."); - mechanism[name] = f - for _, backend_name in ipairs(backend) + mechanisms[name] = f + for _, backend_name in ipairs(backends) do + if backend_mechanism[backend_name] == nil then backend_mechanism[backend_name] = {}; end + t_insert(backend_mechanism[backend_name], name); + end end -- create a new SASL object which can be used to authenticate clients function new(realm, profile) - sasl_i = {}; - + sasl_i = {profile = profile}; return setmetatable(sasl_i, method); end -- get a list of possible SASL mechanims to use function method:mechanisms() - + local mechanisms = {} + for backend, f in pairs(self.profile) do + print(backend) + if backend_mechanism[backend] then + for _, mechanism in ipairs(backend_mechanism[backend]) do + mechanisms[mechanism] = true; + end + end + end + return array.collect(keys(mechanisms)); end -- select a mechanism to use -function method.select( mechanism ) +function method:select(mechanism) + +end + +-- feed new messages to process into the library +function method:process(message) + +end + +--========================= +--SASL PLAIN +local function sasl_mechanism_plain(realm, credentials_handler) + local object = { mechanism = "PLAIN", realm = realm, credentials_handler = credentials_handler} + function object.feed(self, message) + if message == "" or message == nil then return "failure", "malformed-request" end + local response = message + local authorization = s_match(response, "([^&%z]+)") + local authentication = s_match(response, "%z([^&%z]+)%z") + local password = s_match(response, "%z[^&%z]+%z([^&%z]+)") + + if authentication == nil or password == nil then return "failure", "malformed-request" end + self.username = authentication + local auth_success = self.credentials_handler("PLAIN", self.username, self.realm, password) + if auth_success then + return "success" + elseif auth_success == nil then + return "failure", "account-disabled" + else + return "failure", "not-authorized" + end + end + return object end +registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); return _M; -- cgit v1.2.3 From f39e7b794bccd19deba6241b45d4050c2762b416 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sun, 16 Aug 2009 23:20:02 +0200 Subject: Adding some docu. --- util/sasl.lua | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/util/sasl.lua b/util/sasl.lua index d6ac5c1e..772e2dd5 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -37,6 +37,30 @@ local keys = keys local array = require "util.array" module "sasl" +--[[ +Authentication Backend Prototypes: + +plain: + function(username, realm) + return password, state; + end + +plain-test: + function(username, realm, password) + return true or false, state; + end + +digest-md5: + function(username, realm, encoding) + return digesthash, state; + end + +digest-md5-test: + function(username, realm, encoding, digesthash) + return true or false, state; + end +]] + local method = {}; method.__index = method; local mechanisms = {}; @@ -71,6 +95,7 @@ function method:mechanisms() end end end + self["possible_mechanisms"] = mechanisms; return array.collect(keys(mechanisms)); end -- cgit v1.2.3 From 58d9ad4e7e1ea0e58e2bbc580c28f893690a0ae1 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 27 Aug 2009 21:29:36 +0200 Subject: Adjust SASL PLAIN mechanism to the new API. --- util/sasl.lua | 53 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index 772e2dd5..9f7bab20 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -101,38 +101,45 @@ end -- select a mechanism to use function method:select(mechanism) - + self.mech_i = mechanisms[mechanism] + if self.mech_i == nil then return false; end + return true; end -- feed new messages to process into the library function method:process(message) - + if message == "" or message == nil then return "failure", "malformed-request" end + return self.mech_i(self, message); end --========================= --SASL PLAIN -local function sasl_mechanism_plain(realm, credentials_handler) - local object = { mechanism = "PLAIN", realm = realm, credentials_handler = credentials_handler} - function object.feed(self, message) - if message == "" or message == nil then return "failure", "malformed-request" end - local response = message - local authorization = s_match(response, "([^&%z]+)") - local authentication = s_match(response, "%z([^&%z]+)%z") - local password = s_match(response, "%z[^&%z]+%z([^&%z]+)") - - if authentication == nil or password == nil then return "failure", "malformed-request" end - self.username = authentication - local auth_success = self.credentials_handler("PLAIN", self.username, self.realm, password) - - if auth_success then - return "success" - elseif auth_success == nil then - return "failure", "account-disabled" - else - return "failure", "not-authorized" - end +local function sasl_mechanism_plain(self, message) + local response = message + local authorization = s_match(response, "([^&%z]+)") + local authentication = s_match(response, "%z([^&%z]+)%z") + local password = s_match(response, "%z[^&%z]+%z([^&%z]+)") + + if authentication == nil or password == nil then return "failure", "malformed-request" end + + local correct, state = false, false, false; + if self.profile.plain then + local correct_password, state = self.profile.plain(authentication, self.realm); + if correct_password == password then correct = true; else correct = false; end + else if self.profile.plain_test then + correct, state = self.profile.plain_test(authentication, self.realm, password); + end + + self.username = authentication + if not state then + return "failure", "account-disabled"; + end + + if correct then + return "success"; + else + return "failure", "not-authorized"; end - return object end registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); -- cgit v1.2.3 From 1d2b8a073bfb81c0e70732d273bcede5bd6ce67c Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 28 Aug 2009 13:04:38 +0200 Subject: Making mod_saslauth use the new SASL API. --- plugins/mod_saslauth.lua | 18 ++++++++++++------ util/sasl.lua | 18 ++++++++++++------ 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 8d3b4ae4..ec3857b8 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -34,6 +34,12 @@ local xmlns_stanzas ='urn:ietf:params:xml:ns:xmpp-stanzas'; local new_sasl = require "util.sasl".new; +default_authentication_profile = { + plain = function(username, realm) + return usermanager_get_password(username, realm), true; + end +}; + local function build_reply(status, ret, err_msg) local reply = st.stanza(status, {xmlns = xmlns_sasl}); if status == "challenge" then @@ -101,8 +107,8 @@ local function sasl_handler(session, stanza) elseif stanza.attr.mechanism == "ANONYMOUS" then return session.send(build_reply("failure", "mechanism-too-weak")); end - session.sasl_handler = new_sasl(stanza.attr.mechanism, session.host, credentials_callback); - if not session.sasl_handler then + local valid_mechanism = session.sasl_handler:select(stanza.attr.mechanism); + if not valid_mechanism then return session.send(build_reply("failure", "invalid-mechanism")); end elseif not session.sasl_handler then @@ -118,7 +124,7 @@ local function sasl_handler(session, stanza) return; end end - local status, ret, err_msg = session.sasl_handler:feed(text); + local status, ret, err_msg = session.sasl_handler:process(text); handle_status(session, status); local s = build_reply(status, ret, err_msg); log("debug", "sasl reply: %s", tostring(s)); @@ -138,14 +144,14 @@ module:add_event_hook("stream-features", if secure_auth_only and not session.secure then return; end + session.sasl_handler = new_sasl(session.host, default_authentication_profile); features:tag("mechanisms", mechanisms_attr); -- TODO: Provide PLAIN only if TLS is active, this is a SHOULD from the introduction of RFC 4616. This behavior could be overridden via configuration but will issuing a warning or so. if config.get(session.host or "*", "core", "anonymous_login") then features:tag("mechanism"):text("ANONYMOUS"):up(); else - mechanisms = usermanager_get_supported_methods(session.host or "*"); - for k, v in pairs(mechanisms) do - features:tag("mechanism"):text(k):up(); + for k, v in pairs(session.sasl_handler:mechanisms()) do + features:tag("mechanism"):text(v):up(); end end features:up(); diff --git a/util/sasl.lua b/util/sasl.lua index 9f7bab20..687878c4 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -81,6 +81,7 @@ end -- create a new SASL object which can be used to authenticate clients function new(realm, profile) sasl_i = {profile = profile}; + sasl_i.realm = realm; return setmetatable(sasl_i, method); end @@ -92,7 +93,7 @@ function method:mechanisms() if backend_mechanism[backend] then for _, mechanism in ipairs(backend_mechanism[backend]) do mechanisms[mechanism] = true; - end + end end end self["possible_mechanisms"] = mechanisms; @@ -102,7 +103,9 @@ end -- select a mechanism to use function method:select(mechanism) self.mech_i = mechanisms[mechanism] - if self.mech_i == nil then return false; end + if self.mech_i == nil then + return false; + end return true; end @@ -120,13 +123,16 @@ local function sasl_mechanism_plain(self, message) local authentication = s_match(response, "%z([^&%z]+)%z") local password = s_match(response, "%z[^&%z]+%z([^&%z]+)") - if authentication == nil or password == nil then return "failure", "malformed-request" end + if authentication == nil or password == nil then + return "failure", "malformed-request"; + end - local correct, state = false, false, false; + local correct, state = false, false; if self.profile.plain then - local correct_password, state = self.profile.plain(authentication, self.realm); + local correct_password; + correct_password, state = self.profile.plain(authentication, self.realm); if correct_password == password then correct = true; else correct = false; end - else if self.profile.plain_test then + elseif self.profile.plain_test then correct, state = self.profile.plain_test(authentication, self.realm, password); end -- cgit v1.2.3 From cc716d31b6b0e2a6f764fd19832175e30a981c75 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 28 Aug 2009 19:20:12 +0200 Subject: Allow ampersands in passwords for SASL PLAIN mechanism. --- util/sasl.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index 687878c4..e7d90704 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -119,9 +119,9 @@ end --SASL PLAIN local function sasl_mechanism_plain(self, message) local response = message - local authorization = s_match(response, "([^&%z]+)") - local authentication = s_match(response, "%z([^&%z]+)%z") - local password = s_match(response, "%z[^&%z]+%z([^&%z]+)") + local authorization = s_match(response, "([^%z]+)") + local authentication = s_match(response, "%z([^%z]+)%z") + local password = s_match(response, "%z[^%z]+%z([^%z]+)") if authentication == nil or password == nil then return "failure", "malformed-request"; -- cgit v1.2.3 From c73807ad0892ac4d205740786ae2fbfcd351e03f Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 28 Aug 2009 19:43:33 +0200 Subject: List RFC numbers. --- util/sasl.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/util/sasl.lua b/util/sasl.lua index e7d90704..8cb85033 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -116,7 +116,7 @@ function method:process(message) end --========================= ---SASL PLAIN +--SASL PLAIN according to RFC 4616 local function sasl_mechanism_plain(self, message) local response = message local authorization = s_match(response, "([^%z]+)") @@ -149,4 +149,6 @@ local function sasl_mechanism_plain(self, message) end registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); +--========================= +--SASL DIGEST-MD5 return _M; -- cgit v1.2.3 From 9b4aee096ab526579a3b7822b4825d129b0bb1a3 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 28 Aug 2009 19:56:54 +0200 Subject: Importing SASL Digest-MD5 code. --- util/sasl.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/sasl.lua b/util/sasl.lua index 8cb85033..28562d3f 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -150,5 +150,5 @@ end registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); --========================= ---SASL DIGEST-MD5 +--SASL DIGEST-MD5 according to RFC 2831 return _M; -- cgit v1.2.3 From 650d48ce72b44d85828c5d9b36616945a19052ff Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 28 Aug 2009 19:57:09 +0200 Subject: Importing SASL Digest-MD5 code. Now for real. --- util/sasl.lua | 182 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/util/sasl.lua b/util/sasl.lua index 28562d3f..6fd45ce1 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -151,4 +151,186 @@ registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); --========================= --SASL DIGEST-MD5 according to RFC 2831 +local function new_digest_md5(realm, credentials_handler) + --TODO complete support for authzid + + local function serialize(message) + local data = "" + + if type(message) ~= "table" then error("serialize needs an argument of type table.") end + + -- testing all possible values + if message["realm"] then data = data..[[realm="]]..message.realm..[[",]] end + if message["nonce"] then data = data..[[nonce="]]..message.nonce..[[",]] end + if message["qop"] then data = data..[[qop="]]..message.qop..[[",]] end + if message["charset"] then data = data..[[charset=]]..message.charset.."," end + if message["algorithm"] then data = data..[[algorithm=]]..message.algorithm.."," end + if message["rspauth"] then data = data..[[rspauth=]]..message.rspauth.."," end + data = data:gsub(",$", "") + return data + end + + local function utf8tolatin1ifpossible(passwd) + local i = 1; + while i <= #passwd do + local passwd_i = to_byte(passwd:sub(i, i)); + if passwd_i > 0x7F then + if passwd_i < 0xC0 or passwd_i > 0xC3 then + return passwd; + end + i = i + 1; + passwd_i = to_byte(passwd:sub(i, i)); + if passwd_i < 0x80 or passwd_i > 0xBF then + return passwd; + end + end + i = i + 1; + end + + local p = {}; + local j = 0; + i = 1; + while (i <= #passwd) do + local passwd_i = to_byte(passwd:sub(i, i)); + if passwd_i > 0x7F then + i = i + 1; + local passwd_i_1 = to_byte(passwd:sub(i, i)); + t_insert(p, to_char(passwd_i%4*64 + passwd_i_1%64)); -- I'm so clever + else + t_insert(p, to_char(passwd_i)); + end + i = i + 1; + end + return t_concat(p); + end + local function latin1toutf8(str) + local p = {}; + for ch in gmatch(str, ".") do + ch = to_byte(ch); + if (ch < 0x80) then + t_insert(p, to_char(ch)); + elseif (ch < 0xC0) then + t_insert(p, to_char(0xC2, ch)); + else + t_insert(p, to_char(0xC3, ch - 64)); + end + end + return t_concat(p); + end + local function parse(data) + local message = {} + for k, v in gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder + message[k] = v; + end + return message; + end + + local object = { mechanism = "DIGEST-MD5", realm = realm, credentials_handler = credentials_handler}; + + object.nonce = generate_uuid(); + object.step = 0; + object.nonce_count = {}; + + function object.feed(self, message) + self.step = self.step + 1; + if (self.step == 1) then + local challenge = serialize({ nonce = object.nonce, + qop = "auth", + charset = "utf-8", + algorithm = "md5-sess", + realm = self.realm}); + return "challenge", challenge; + elseif (self.step == 2) then + local response = parse(message); + -- check for replay attack + if response["nc"] then + if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end + end + + -- check for username, it's REQUIRED by RFC 2831 + if not response["username"] then + return "failure", "malformed-request"; + end + self["username"] = response["username"]; + + -- check for nonce, ... + if not response["nonce"] then + return "failure", "malformed-request"; + else + -- check if it's the right nonce + if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end + end + + if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end + if not response["qop"] then response["qop"] = "auth" end + + if response["realm"] == nil or response["realm"] == "" then + response["realm"] = ""; + elseif response["realm"] ~= self.realm then + return "failure", "not-authorized", "Incorrect realm value"; + end + + local decoder; + if response["charset"] == nil then + decoder = utf8tolatin1ifpossible; + elseif response["charset"] ~= "utf-8" then + return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; + end + + local domain = ""; + local protocol = ""; + if response["digest-uri"] then + protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); + if protocol == nil or domain == nil then return "failure", "malformed-request" end + else + return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." + end + + --TODO maybe realm support + self.username = response["username"]; + local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); + if Y == nil then return "failure", "not-authorized" + elseif Y == false then return "failure", "account-disabled" end + local A1 = ""; + if response.authzid then + if response.authzid == self.username.."@"..self.realm then + -- COMPAT + log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); + A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; + else + A1 = "?"; + end + else + A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; + end + local A2 = "AUTHENTICATE:"..protocol.."/"..domain; + + local HA1 = md5(A1, true); + local HA2 = md5(A2, true); + + local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; + local response_value = md5(KD, true); + + if response_value == response["response"] then + -- calculate rspauth + A2 = ":"..protocol.."/"..domain; + + HA1 = md5(A1, true); + HA2 = md5(A2, true); + + KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 + local rspauth = md5(KD, true); + self.authenticated = true; + return "challenge", serialize({rspauth = rspauth}); + else + return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." + end + elseif self.step == 3 then + if self.authenticated ~= nil then return "success" + else return "failure", "malformed-request" end + end + end + return object; +end + return _M; -- cgit v1.2.3 From 2c8f4d3ed84300f22b1b66d4e795e6478500e047 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 28 Aug 2009 22:01:58 +0200 Subject: Store stage in SASL object. --- util/sasl.lua | 179 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 88 insertions(+), 91 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index 6fd45ce1..6b14c1b1 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -151,7 +151,7 @@ registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); --========================= --SASL DIGEST-MD5 according to RFC 2831 -local function new_digest_md5(realm, credentials_handler) +local function sasl_mechanism_digest_md5(self, message) --TODO complete support for authzid local function serialize(message) @@ -225,112 +225,109 @@ local function new_digest_md5(realm, credentials_handler) return message; end - local object = { mechanism = "DIGEST-MD5", realm = realm, credentials_handler = credentials_handler}; - - object.nonce = generate_uuid(); - object.step = 0; - object.nonce_count = {}; - - function object.feed(self, message) - self.step = self.step + 1; - if (self.step == 1) then - local challenge = serialize({ nonce = object.nonce, - qop = "auth", - charset = "utf-8", - algorithm = "md5-sess", - realm = self.realm}); - return "challenge", challenge; - elseif (self.step == 2) then - local response = parse(message); - -- check for replay attack - if response["nc"] then - if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end - end + if not self.nonce then + self.nonce = generate_uuid(); + self.step = 0; + self.nonce_count = {}; + end - -- check for username, it's REQUIRED by RFC 2831 - if not response["username"] then - return "failure", "malformed-request"; - end - self["username"] = response["username"]; + self.step = self.step + 1; + if (self.step == 1) then + local challenge = serialize({ nonce = object.nonce, + qop = "auth", + charset = "utf-8", + algorithm = "md5-sess", + realm = self.realm}); + return "challenge", challenge; + elseif (self.step == 2) then + local response = parse(message); + -- check for replay attack + if response["nc"] then + if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end + end - -- check for nonce, ... - if not response["nonce"] then - return "failure", "malformed-request"; - else - -- check if it's the right nonce - if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end - end + -- check for username, it's REQUIRED by RFC 2831 + if not response["username"] then + return "failure", "malformed-request"; + end + self["username"] = response["username"]; + + -- check for nonce, ... + if not response["nonce"] then + return "failure", "malformed-request"; + else + -- check if it's the right nonce + if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end + end - if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end - if not response["qop"] then response["qop"] = "auth" end + if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end + if not response["qop"] then response["qop"] = "auth" end - if response["realm"] == nil or response["realm"] == "" then - response["realm"] = ""; - elseif response["realm"] ~= self.realm then - return "failure", "not-authorized", "Incorrect realm value"; - end + if response["realm"] == nil or response["realm"] == "" then + response["realm"] = ""; + elseif response["realm"] ~= self.realm then + return "failure", "not-authorized", "Incorrect realm value"; + end - local decoder; - if response["charset"] == nil then - decoder = utf8tolatin1ifpossible; - elseif response["charset"] ~= "utf-8" then - return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; - end + local decoder; + if response["charset"] == nil then + decoder = utf8tolatin1ifpossible; + elseif response["charset"] ~= "utf-8" then + return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; + end - local domain = ""; - local protocol = ""; - if response["digest-uri"] then - protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); - if protocol == nil or domain == nil then return "failure", "malformed-request" end - else - return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." - end + local domain = ""; + local protocol = ""; + if response["digest-uri"] then + protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); + if protocol == nil or domain == nil then return "failure", "malformed-request" end + else + return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." + end - --TODO maybe realm support - self.username = response["username"]; - local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); - if Y == nil then return "failure", "not-authorized" - elseif Y == false then return "failure", "account-disabled" end - local A1 = ""; - if response.authzid then - if response.authzid == self.username.."@"..self.realm then - -- COMPAT - log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; - else - A1 = "?"; - end + --TODO maybe realm support + self.username = response["username"]; + local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); + if Y == nil then return "failure", "not-authorized" + elseif Y == false then return "failure", "account-disabled" end + local A1 = ""; + if response.authzid then + if response.authzid == self.username.."@"..self.realm then + -- COMPAT + log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); + A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; else - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; + A1 = "?"; end - local A2 = "AUTHENTICATE:"..protocol.."/"..domain; + else + A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; + end + local A2 = "AUTHENTICATE:"..protocol.."/"..domain; - local HA1 = md5(A1, true); - local HA2 = md5(A2, true); + local HA1 = md5(A1, true); + local HA2 = md5(A2, true); - local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; - local response_value = md5(KD, true); + local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; + local response_value = md5(KD, true); - if response_value == response["response"] then - -- calculate rspauth - A2 = ":"..protocol.."/"..domain; + if response_value == response["response"] then + -- calculate rspauth + A2 = ":"..protocol.."/"..domain; - HA1 = md5(A1, true); - HA2 = md5(A2, true); + HA1 = md5(A1, true); + HA2 = md5(A2, true); - KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 - local rspauth = md5(KD, true); - self.authenticated = true; - return "challenge", serialize({rspauth = rspauth}); - else - return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." - end - elseif self.step == 3 then - if self.authenticated ~= nil then return "success" - else return "failure", "malformed-request" end + KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 + local rspauth = md5(KD, true); + self.authenticated = true; + return "challenge", serialize({rspauth = rspauth}); + else + return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." end + elseif self.step == 3 then + if self.authenticated ~= nil then return "success" + else return "failure", "malformed-request" end end - return object; end return _M; -- cgit v1.2.3 From 85fb108f5dd540f3ea9b3920cc8b5e1a40834c44 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 28 Aug 2009 22:03:11 +0200 Subject: Fail if mechanism has already been selected. --- util/sasl.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/util/sasl.lua b/util/sasl.lua index 6b14c1b1..36f43ec2 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -102,6 +102,10 @@ end -- select a mechanism to use function method:select(mechanism) + if self.mech_i then + return false; + end + self.mech_i = mechanisms[mechanism] if self.mech_i == nil then return false; -- cgit v1.2.3 From 5554c334177b01d969cd5feb4d73ec2af0c80e7a Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 12 Nov 2009 21:57:37 +0100 Subject: Move each mechanism in an own file. --- util/sasl.lua | 218 +---------------------------------------------- util/sasl/digest-md5.lua | 194 +++++++++++++++++++++++++++++++++++++++++ util/sasl/plain.lua | 53 ++++++++++++ 3 files changed, 251 insertions(+), 214 deletions(-) create mode 100644 util/sasl/digest-md5.lua create mode 100644 util/sasl/plain.lua diff --git a/util/sasl.lua b/util/sasl.lua index 36f43ec2..94ed3ac9 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -119,219 +119,9 @@ function method:process(message) return self.mech_i(self, message); end ---========================= ---SASL PLAIN according to RFC 4616 -local function sasl_mechanism_plain(self, message) - local response = message - local authorization = s_match(response, "([^%z]+)") - local authentication = s_match(response, "%z([^%z]+)%z") - local password = s_match(response, "%z[^%z]+%z([^%z]+)") - - if authentication == nil or password == nil then - return "failure", "malformed-request"; - end - - local correct, state = false, false; - if self.profile.plain then - local correct_password; - correct_password, state = self.profile.plain(authentication, self.realm); - if correct_password == password then correct = true; else correct = false; end - elseif self.profile.plain_test then - correct, state = self.profile.plain_test(authentication, self.realm, password); - end - - self.username = authentication - if not state then - return "failure", "account-disabled"; - end - - if correct then - return "success"; - else - return "failure", "not-authorized"; - end -end -registerMechanism("PLAIN", {"plain", "plain_test"}, sasl_mechanism_plain); - ---========================= ---SASL DIGEST-MD5 according to RFC 2831 -local function sasl_mechanism_digest_md5(self, message) - --TODO complete support for authzid - - local function serialize(message) - local data = "" - - if type(message) ~= "table" then error("serialize needs an argument of type table.") end - - -- testing all possible values - if message["realm"] then data = data..[[realm="]]..message.realm..[[",]] end - if message["nonce"] then data = data..[[nonce="]]..message.nonce..[[",]] end - if message["qop"] then data = data..[[qop="]]..message.qop..[[",]] end - if message["charset"] then data = data..[[charset=]]..message.charset.."," end - if message["algorithm"] then data = data..[[algorithm=]]..message.algorithm.."," end - if message["rspauth"] then data = data..[[rspauth=]]..message.rspauth.."," end - data = data:gsub(",$", "") - return data - end - - local function utf8tolatin1ifpossible(passwd) - local i = 1; - while i <= #passwd do - local passwd_i = to_byte(passwd:sub(i, i)); - if passwd_i > 0x7F then - if passwd_i < 0xC0 or passwd_i > 0xC3 then - return passwd; - end - i = i + 1; - passwd_i = to_byte(passwd:sub(i, i)); - if passwd_i < 0x80 or passwd_i > 0xBF then - return passwd; - end - end - i = i + 1; - end - - local p = {}; - local j = 0; - i = 1; - while (i <= #passwd) do - local passwd_i = to_byte(passwd:sub(i, i)); - if passwd_i > 0x7F then - i = i + 1; - local passwd_i_1 = to_byte(passwd:sub(i, i)); - t_insert(p, to_char(passwd_i%4*64 + passwd_i_1%64)); -- I'm so clever - else - t_insert(p, to_char(passwd_i)); - end - i = i + 1; - end - return t_concat(p); - end - local function latin1toutf8(str) - local p = {}; - for ch in gmatch(str, ".") do - ch = to_byte(ch); - if (ch < 0x80) then - t_insert(p, to_char(ch)); - elseif (ch < 0xC0) then - t_insert(p, to_char(0xC2, ch)); - else - t_insert(p, to_char(0xC3, ch - 64)); - end - end - return t_concat(p); - end - local function parse(data) - local message = {} - for k, v in gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder - message[k] = v; - end - return message; - end - - if not self.nonce then - self.nonce = generate_uuid(); - self.step = 0; - self.nonce_count = {}; - end - - self.step = self.step + 1; - if (self.step == 1) then - local challenge = serialize({ nonce = object.nonce, - qop = "auth", - charset = "utf-8", - algorithm = "md5-sess", - realm = self.realm}); - return "challenge", challenge; - elseif (self.step == 2) then - local response = parse(message); - -- check for replay attack - if response["nc"] then - if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end - end - - -- check for username, it's REQUIRED by RFC 2831 - if not response["username"] then - return "failure", "malformed-request"; - end - self["username"] = response["username"]; - - -- check for nonce, ... - if not response["nonce"] then - return "failure", "malformed-request"; - else - -- check if it's the right nonce - if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end - end - - if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end - if not response["qop"] then response["qop"] = "auth" end - - if response["realm"] == nil or response["realm"] == "" then - response["realm"] = ""; - elseif response["realm"] ~= self.realm then - return "failure", "not-authorized", "Incorrect realm value"; - end - - local decoder; - if response["charset"] == nil then - decoder = utf8tolatin1ifpossible; - elseif response["charset"] ~= "utf-8" then - return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; - end - - local domain = ""; - local protocol = ""; - if response["digest-uri"] then - protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); - if protocol == nil or domain == nil then return "failure", "malformed-request" end - else - return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." - end - - --TODO maybe realm support - self.username = response["username"]; - local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); - if Y == nil then return "failure", "not-authorized" - elseif Y == false then return "failure", "account-disabled" end - local A1 = ""; - if response.authzid then - if response.authzid == self.username.."@"..self.realm then - -- COMPAT - log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; - else - A1 = "?"; - end - else - A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; - end - local A2 = "AUTHENTICATE:"..protocol.."/"..domain; - - local HA1 = md5(A1, true); - local HA2 = md5(A2, true); - - local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; - local response_value = md5(KD, true); - - if response_value == response["response"] then - -- calculate rspauth - A2 = ":"..protocol.."/"..domain; - - HA1 = md5(A1, true); - HA2 = md5(A2, true); - - KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 - local rspauth = md5(KD, true); - self.authenticated = true; - return "challenge", serialize({rspauth = rspauth}); - else - return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." - end - elseif self.step == 3 then - if self.authenticated ~= nil then return "success" - else return "failure", "malformed-request" end - end -end +-- load the mechanisms +require "sasl.plain" +require "sasl.digest-md5" +require "sasl.scram" return _M; diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua new file mode 100644 index 00000000..8acbce4b --- /dev/null +++ b/util/sasl/digest-md5.lua @@ -0,0 +1,194 @@ +-- sasl.lua v0.4 +-- Copyright (C) 2008-2009 Tobias Markmann +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +-- * Neither the name of Tobias Markmann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +--========================= +--SASL DIGEST-MD5 according to RFC 2831 +local function sasl_mechanism_digest_md5(self, message) + --TODO complete support for authzid + + local function serialize(message) + local data = "" + + if type(message) ~= "table" then error("serialize needs an argument of type table.") end + + -- testing all possible values + if message["realm"] then data = data..[[realm="]]..message.realm..[[",]] end + if message["nonce"] then data = data..[[nonce="]]..message.nonce..[[",]] end + if message["qop"] then data = data..[[qop="]]..message.qop..[[",]] end + if message["charset"] then data = data..[[charset=]]..message.charset.."," end + if message["algorithm"] then data = data..[[algorithm=]]..message.algorithm.."," end + if message["rspauth"] then data = data..[[rspauth=]]..message.rspauth.."," end + data = data:gsub(",$", "") + return data + end + + local function utf8tolatin1ifpossible(passwd) + local i = 1; + while i <= #passwd do + local passwd_i = to_byte(passwd:sub(i, i)); + if passwd_i > 0x7F then + if passwd_i < 0xC0 or passwd_i > 0xC3 then + return passwd; + end + i = i + 1; + passwd_i = to_byte(passwd:sub(i, i)); + if passwd_i < 0x80 or passwd_i > 0xBF then + return passwd; + end + end + i = i + 1; + end + + local p = {}; + local j = 0; + i = 1; + while (i <= #passwd) do + local passwd_i = to_byte(passwd:sub(i, i)); + if passwd_i > 0x7F then + i = i + 1; + local passwd_i_1 = to_byte(passwd:sub(i, i)); + t_insert(p, to_char(passwd_i%4*64 + passwd_i_1%64)); -- I'm so clever + else + t_insert(p, to_char(passwd_i)); + end + i = i + 1; + end + return t_concat(p); + end + local function latin1toutf8(str) + local p = {}; + for ch in gmatch(str, ".") do + ch = to_byte(ch); + if (ch < 0x80) then + t_insert(p, to_char(ch)); + elseif (ch < 0xC0) then + t_insert(p, to_char(0xC2, ch)); + else + t_insert(p, to_char(0xC3, ch - 64)); + end + end + return t_concat(p); + end + local function parse(data) + local message = {} + for k, v in gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder + message[k] = v; + end + return message; + end + + if not self.nonce then + self.nonce = generate_uuid(); + self.step = 0; + self.nonce_count = {}; + end + + self.step = self.step + 1; + if (self.step == 1) then + local challenge = serialize({ nonce = object.nonce, + qop = "auth", + charset = "utf-8", + algorithm = "md5-sess", + realm = self.realm}); + return "challenge", challenge; + elseif (self.step == 2) then + local response = parse(message); + -- check for replay attack + if response["nc"] then + if self.nonce_count[response["nc"]] then return "failure", "not-authorized" end + end + + -- check for username, it's REQUIRED by RFC 2831 + if not response["username"] then + return "failure", "malformed-request"; + end + self["username"] = response["username"]; + + -- check for nonce, ... + if not response["nonce"] then + return "failure", "malformed-request"; + else + -- check if it's the right nonce + if response["nonce"] ~= tostring(self.nonce) then return "failure", "malformed-request" end + end + + if not response["cnonce"] then return "failure", "malformed-request", "Missing entry for cnonce in SASL message." end + if not response["qop"] then response["qop"] = "auth" end + + if response["realm"] == nil or response["realm"] == "" then + response["realm"] = ""; + elseif response["realm"] ~= self.realm then + return "failure", "not-authorized", "Incorrect realm value"; + end + + local decoder; + if response["charset"] == nil then + decoder = utf8tolatin1ifpossible; + elseif response["charset"] ~= "utf-8" then + return "failure", "incorrect-encoding", "The client's response uses "..response["charset"].." for encoding with isn't supported by sasl.lua. Supported encodings are latin or utf-8."; + end + + local domain = ""; + local protocol = ""; + if response["digest-uri"] then + protocol, domain = response["digest-uri"]:match("(%w+)/(.*)$"); + if protocol == nil or domain == nil then return "failure", "malformed-request" end + else + return "failure", "malformed-request", "Missing entry for digest-uri in SASL message." + end + + --TODO maybe realm support + self.username = response["username"]; + local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); + if Y == nil then return "failure", "not-authorized" + elseif Y == false then return "failure", "account-disabled" end + local A1 = ""; + if response.authzid then + if response.authzid == self.username.."@"..self.realm then + -- COMPAT + log("warn", "Client is violating XMPP RFC. See section 6.1 of RFC 3920."); + A1 = Y..":"..response["nonce"]..":"..response["cnonce"]..":"..response.authzid; + else + A1 = "?"; + end + else + A1 = Y..":"..response["nonce"]..":"..response["cnonce"]; + end + local A2 = "AUTHENTICATE:"..protocol.."/"..domain; + + local HA1 = md5(A1, true); + local HA2 = md5(A2, true); + + local KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2; + local response_value = md5(KD, true); + + if response_value == response["response"] then + -- calculate rspauth + A2 = ":"..protocol.."/"..domain; + + HA1 = md5(A1, true); + HA2 = md5(A2, true); + + KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 + local rspauth = md5(KD, true); + self.authenticated = true; + return "challenge", serialize({rspauth = rspauth}); + else + return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." + end + elseif self.step == 3 then + if self.authenticated ~= nil then return "success" + else return "failure", "malformed-request" end + end +end \ No newline at end of file diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua new file mode 100644 index 00000000..898657f0 --- /dev/null +++ b/util/sasl/plain.lua @@ -0,0 +1,53 @@ +-- sasl.lua v0.4 +-- Copyright (C) 2008-2009 Tobias Markmann +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +-- * Neither the name of Tobias Markmann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +local registerMechanism = registerMechanism + +module "plain" + +--========================= +--SASL PLAIN according to RFC 4616 +local function plain(self, message) + local response = message + local authorization = s_match(response, "([^%z]+)") + local authentication = s_match(response, "%z([^%z]+)%z") + local password = s_match(response, "%z[^%z]+%z([^%z]+)") + + if authentication == nil or password == nil then + return "failure", "malformed-request"; + end + + local correct, state = false, false; + if self.profile.plain then + local correct_password; + correct_password, state = self.profile.plain(authentication, self.realm); + if correct_password == password then correct = true; else correct = false; end + elseif self.profile.plain_test then + correct, state = self.profile.plain_test(authentication, self.realm, password); + end + + self.username = authentication + if not state then + return "failure", "account-disabled"; + end + + if correct then + return "success"; + else + return "failure", "not-authorized"; + end +end + +registerMechanism("PLAIN", {"plain", "plain_test"}, plain); + +return _M; \ No newline at end of file -- cgit v1.2.3 From 72e185fa03df69754ea24322db832e85b015c379 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 13 Nov 2009 09:21:19 +0100 Subject: Getting PLAIN mechanism work with the new API. --- util/sasl.lua | 9 ++++++--- util/sasl/plain.lua | 6 ++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index 94ed3ac9..e9b466ee 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -30,6 +30,8 @@ local error = error local print = print local setmetatable = setmetatable; local assert = assert; +local dofile = dofile; +local require = require; require "util.iterators" local keys = keys @@ -120,8 +122,9 @@ function method:process(message) end -- load the mechanisms -require "sasl.plain" -require "sasl.digest-md5" -require "sasl.scram" +m = require "util.sasl.plain" +m.init(registerMechanism) +--dofile "util/sasl/digest-md5.lua" +--dofile "util/sasl/scram.lua" return _M; diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 898657f0..08056053 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -11,7 +11,7 @@ -- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -local registerMechanism = registerMechanism +local s_match = string.match; module "plain" @@ -48,6 +48,8 @@ local function plain(self, message) end end -registerMechanism("PLAIN", {"plain", "plain_test"}, plain); +function init(registerMechanism) + registerMechanism("PLAIN", {"plain", "plain_test"}, plain); +end return _M; \ No newline at end of file -- cgit v1.2.3 From ed841d20a7881697a28a5497067df53f128348e3 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 13 Nov 2009 10:54:17 +0100 Subject: Add support for plain profile in digest-md5 implementation. --- util/sasl.lua | 18 ++++++++++------- util/sasl/digest-md5.lua | 51 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 54 insertions(+), 15 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index e9b466ee..8fe4727e 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -16,10 +16,8 @@ local md5 = require "util.hashes".md5; local log = require "util.logger".init("sasl"); local tostring = tostring; local st = require "util.stanza"; -local generate_uuid = require "util.uuid".generate; local pairs, ipairs = pairs, ipairs; local t_insert, t_concat = table.insert, table.concat; -local to_byte, to_char = string.byte, string.char; local to_unicode = require "util.encodings".idna.to_unicode; local s_match = string.match; local gmatch = string.gmatch @@ -42,6 +40,10 @@ module "sasl" --[[ Authentication Backend Prototypes: +state = false : disabled +state = true : enabled +state = nil : non-existant + plain: function(username, realm) return password, state; @@ -117,14 +119,16 @@ end -- feed new messages to process into the library function method:process(message) - if message == "" or message == nil then return "failure", "malformed-request" end + --if message == "" or message == nil then return "failure", "malformed-request" end return self.mech_i(self, message); end -- load the mechanisms -m = require "util.sasl.plain" -m.init(registerMechanism) ---dofile "util/sasl/digest-md5.lua" ---dofile "util/sasl/scram.lua" +load_mechs = {"plain", "digest-md5"} +for _, mech in ipairs(load_mechs) do + local name = "util.sasl."..mech; + local m = require(name); + m.init(registerMechanism) +end return _M; diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index 8acbce4b..1ff0f62b 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -11,10 +11,29 @@ -- -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +local tostring = tostring; +local type = type; + +local s_gmatch = string.gmatch; +local s_match = string.match; +local t_concat = table.concat; +local t_insert = table.insert; +local to_byte, to_char = string.byte, string.char; + +local md5 = require "util.hashes".md5; +local log = require "util.logger".init("sasl"); +local generate_uuid = require "util.uuid".generate; + +module "plain" --========================= --SASL DIGEST-MD5 according to RFC 2831 -local function sasl_mechanism_digest_md5(self, message) +local function digest_response() + + return response, A1, A2 +end + +local function digest(self, message) --TODO complete support for authzid local function serialize(message) @@ -68,7 +87,7 @@ local function sasl_mechanism_digest_md5(self, message) end local function latin1toutf8(str) local p = {}; - for ch in gmatch(str, ".") do + for ch in s_gmatch(str, ".") do ch = to_byte(ch); if (ch < 0x80) then t_insert(p, to_char(ch)); @@ -82,7 +101,7 @@ local function sasl_mechanism_digest_md5(self, message) end local function parse(data) local message = {} - for k, v in gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder + for k, v in s_gmatch(data, [[([%w%-]+)="?([^",]*)"?,?]]) do -- FIXME The hacky regex makes me shudder message[k] = v; end return message; @@ -96,7 +115,7 @@ local function sasl_mechanism_digest_md5(self, message) self.step = self.step + 1; if (self.step == 1) then - local challenge = serialize({ nonce = object.nonce, + local challenge = serialize({ nonce = self.nonce, qop = "auth", charset = "utf-8", algorithm = "md5-sess", @@ -150,9 +169,19 @@ local function sasl_mechanism_digest_md5(self, message) --TODO maybe realm support self.username = response["username"]; - local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); - if Y == nil then return "failure", "not-authorized" - elseif Y == false then return "failure", "account-disabled" end + if self.profile.plain then + local password, state = self.profile.plain(response["username"], self.realm) + if state == nil then return "failure", "not-authorized" + elseif state == false then return "failure", "account-disabled" end + Y = md5(response["username"]..":"..self.realm..":"..password); + elseif self.profile["digest-md5"] then + --local Y, state = self.profile["digest-md5"](response["username"], self.realm, response["charset"]) + elseif self.profile["digest-md5-test"] then + + end + --local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); + --if Y == nil then return "failure", "not-authorized" + --elseif Y == false then return "failure", "account-disabled" end local A1 = ""; if response.authzid then if response.authzid == self.username.."@"..self.realm then @@ -191,4 +220,10 @@ local function sasl_mechanism_digest_md5(self, message) if self.authenticated ~= nil then return "success" else return "failure", "malformed-request" end end -end \ No newline at end of file +end + +function init(registerMechanism) + registerMechanism("DIGEST-MD5", {"plain"}, digest); +end + +return _M; \ No newline at end of file -- cgit v1.2.3 From f66117f6255fa37f0e049131ade72232251c32f1 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 13 Nov 2009 11:10:06 +0100 Subject: Broken DIGEST-MD5 client support again. --- util/sasl/digest-md5.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index 1ff0f62b..df0a5b47 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -173,11 +173,11 @@ local function digest(self, message) local password, state = self.profile.plain(response["username"], self.realm) if state == nil then return "failure", "not-authorized" elseif state == false then return "failure", "account-disabled" end - Y = md5(response["username"]..":"..self.realm..":"..password); + Y = md5(response["username"]..":"..response["realm"]..":"..password); elseif self.profile["digest-md5"] then --local Y, state = self.profile["digest-md5"](response["username"], self.realm, response["charset"]) elseif self.profile["digest-md5-test"] then - + -- TODO end --local password_encoding, Y = self.credentials_handler("DIGEST-MD5", response["username"], self.realm, response["realm"], decoder); --if Y == nil then return "failure", "not-authorized" -- cgit v1.2.3 From 8e7427e70d551a051ae178c882c179dec1d891dc Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 13 Nov 2009 11:21:21 +0100 Subject: Change of the digest-md5 profile. --- util/sasl.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/sasl.lua b/util/sasl.lua index 8fe4727e..b07f878b 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -55,7 +55,8 @@ plain-test: end digest-md5: - function(username, realm, encoding) + function(username, domain, realm, encoding) -- domain and realm are usually the same; for some broken + -- implementations it's not return digesthash, state; end -- cgit v1.2.3 From 8c36b99f27aaa438ce12c8183df357d3c6bd916d Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 13 Nov 2009 11:24:22 +0100 Subject: Adding support for digest-md5 profile in DIGEST-MD5 implementation. --- util/sasl.lua | 2 +- util/sasl/digest-md5.lua | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index b07f878b..c7aa050b 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -61,7 +61,7 @@ digest-md5: end digest-md5-test: - function(username, realm, encoding, digesthash) + function(username, domain, realm, encoding, digesthash) return true or false, state; end ]] diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index df0a5b47..3f50e232 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -175,7 +175,9 @@ local function digest(self, message) elseif state == false then return "failure", "account-disabled" end Y = md5(response["username"]..":"..response["realm"]..":"..password); elseif self.profile["digest-md5"] then - --local Y, state = self.profile["digest-md5"](response["username"], self.realm, response["charset"]) + local Y, state = self.profile["digest-md5"](response["username"], self.realm, response["realm"] response["charset"]) + if state == nil then return "failure", "not-authorized" + elseif state == false then return "failure", "account-disabled" end elseif self.profile["digest-md5-test"] then -- TODO end -- cgit v1.2.3 From 9a77245fd19b2421169ed765b4820678073fade2 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Fri, 13 Nov 2009 14:31:03 +0100 Subject: Adding a note for possible round trip savings. --- util/sasl/digest-md5.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index 3f50e232..ba042933 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -214,6 +214,7 @@ local function digest(self, message) KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 local rspauth = md5(KD, true); self.authenticated = true; + --TODO: considering sending the rspauth in a success node for saving one roundtrip; allowed according to http://tools.ietf.org/html/draft-saintandre-rfc3920bis-09#section-7.3.6 return "challenge", serialize({rspauth = rspauth}); else return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." -- cgit v1.2.3 From 9a1b93edc7b012cc75a0f4bf5a5fe64cd300f6d8 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 17 Nov 2009 00:56:41 +0100 Subject: Initial commit of SCRAM SASL mechanism. --- util/sasl/scram.lua | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 util/sasl/scram.lua diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua new file mode 100644 index 00000000..b7507f3e --- /dev/null +++ b/util/sasl/scram.lua @@ -0,0 +1,90 @@ +-- sasl.lua v0.4 +-- Copyright (C) 2008-2009 Tobias Markmann +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +-- * Neither the name of Tobias Markmann nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +local s_match = string.match; + +local base64 = require "util.encodings".base64; +local xor = require "bit".bxor +local hmac_sha1 = require "util.hmac".sha1; +local sha1 = require "util.hashes".sha1; + +module "plain" + +--========================= +--SASL SCRAM-SHA-1 according to draft-ietf-sasl-scram-10 +local default_i = 4096 + +local function bp( b ) + local result = "" + for i=1, b:len() do + result = result.."\\"..b:byte(i) + end + return result +end + +local function binaryXOR( a, b ) + if string.len(a) > string.len(b) then + b = string.rep("\0", a:len() - b:len())..b + elseif string.len(a) < string.len(b) then + a = string.rep("\0", b:len() - a:len())..a + end + local result = "" + for i=1, a:len() do + result = result..string.char(xor(a:byte(i), b:byte(i))) + end + return result +end + +-- hash algorithm independent Hi(PBKDF2) implementation +local function Hi(hmac, str, salt, i) + local Ust = hmac(str, salt.."\0\0\0\1"); + local res = Ust; + for n=1,i-1 do + Und = hmac(str, Ust) + res = binaryXOR(res, Und) + Ust = Und + end + return res +end + +local function validate_username(username) + -- check for forbidden char sequences + + -- replace =2D with , and =3D with = + + -- apply SASLprep + return username; +end + +local function scram_sha_1(self, message) + if not self.state then self["state"] = {} end + + if not self.state.name then + -- we are processing client_first_message + self.state["name"] = string.match(client_first_message, "n=(.+),r=") + self.state["clientnonce"] = string.match(client_first_message, "r=([^,]+)") + + self.state.name = validate_username(self.state.name); + if not self.state.name then + return "failure", "malformed-request"; + end + else + -- we are processing client_final_message + end +end + +function init(registerMechanism) + registerMechanism("SCRAM-SHA-1", {"plain"}, scram_sha_1); +end + +return _M; \ No newline at end of file -- cgit v1.2.3 From edd37a1e348c6f989351cc9b682bbbae4f6a2a1c Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 17 Nov 2009 09:33:15 +0100 Subject: Added missing require for generate_uuid. --- util/sasl/anonymous.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/util/sasl/anonymous.lua b/util/sasl/anonymous.lua index 8f2a7708..65650294 100644 --- a/util/sasl/anonymous.lua +++ b/util/sasl/anonymous.lua @@ -14,6 +14,7 @@ local s_match = string.match; local log = require "util.logger".init("sasl"); +local generate_uuid = require "util.uuid".generate; module "anonymous" -- cgit v1.2.3 From 1fa16fc88c2492df997fa5471eb282e977602c25 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 17 Nov 2009 11:03:54 +0100 Subject: Completed SCRAM-SHA-1 implementation to a ready-to-test state. --- util/sasl/scram.lua | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index b7507f3e..9362cca7 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -17,6 +17,7 @@ local base64 = require "util.encodings".base64; local xor = require "bit".bxor local hmac_sha1 = require "util.hmac".sha1; local sha1 = require "util.hashes".sha1; +local generate_uuid = require "util.uuid".generate; module "plain" @@ -71,15 +72,52 @@ local function scram_sha_1(self, message) if not self.state.name then -- we are processing client_first_message - self.state["name"] = string.match(client_first_message, "n=(.+),r=") - self.state["clientnonce"] = string.match(client_first_message, "r=([^,]+)") + local client_first_message = message; + self.state["name"] = client_first_message:match("n=(.+),r=") + self.state["clientnonce"] = client_first_message:match("r=([^,]+)") self.state.name = validate_username(self.state.name); - if not self.state.name then + if not self.state.name or not self.state.clientnonce then return "failure", "malformed-request"; end + self.state["servernonce"] = generate_uuid(); + self.state["salt"] = generate_uuid(); + + local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..default_i; + return "challenge", server_first_message else -- we are processing client_final_message + local client_final_message = message; + + self.state["proof"] = client_final_message:match("p=(.+)"); + self.state["nonce"] = client_final_message:match("r=(.+),p="); + self.state["channelbinding"] = client_final_message:match("c=(.+),r="); + if not self.state.proof or not self.state.nonce or not self.state.channelbinding then + return "failure", "malformed-request"; + end + + local password; + if self.profile.plain then + password, state = self.profile.plain(self.state.name, self.realm) + if state == nil then return "failure", "not-authorized" + elseif state == false then return "failure", "account-disabled" end + end + + local SaltedPassword = Hi(hmac_sha1, password, self.state.salt, default_i) + local ClientKey = hmac_sha1(SaltedPassword, "Client Key") + local ServerKey = hmac_sha1(SaltedPassword, "Server Key") + local StoredKey = sha1(ClientKey) + local AuthMessage = "n=" .. s_match(client_first_message,"n=(.+)") .. "," .. server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") + local ClientSignature = hmac_sha1(StoredKey, AuthMessage) + local ClientProof = binaryXOR(ClientKey, ClientSignature) + local ServerSignature = hmac_sha1(ServerKey, AuthMessage) + + if base64.encode(ClientProof) == self.state.proof then + local server_final_message = "v="..base64.encode(ServerSignature); + return "success", server_final_message; + else + return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated."; + end end end -- cgit v1.2.3 From 12c7adc2e7a748941853a43102ec73e07d51850b Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 17 Nov 2009 11:31:59 +0100 Subject: Add check for forbidden char sequences in validate_username(). --- util/sasl/scram.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 9362cca7..c3bc9600 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -60,7 +60,8 @@ end local function validate_username(username) -- check for forbidden char sequences - + for eq in s:gmatch("=(.?.?)") do + if eq ~= "2D" and eq ~= "3D" then return false end end return true; -- replace =2D with , and =3D with = -- apply SASLprep -- cgit v1.2.3 From b0f89bf88505895e77bfda26c0e2720c14bf2918 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Tue, 17 Nov 2009 22:39:18 +0100 Subject: Making interop with libpurple. (Thanks darkrain). --- util/sasl.lua | 2 +- util/sasl/scram.lua | 19 ++++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index c8aa16a2..82fc1226 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -125,7 +125,7 @@ function method:process(message) end -- load the mechanisms -load_mechs = {"plain", "digest-md5", "anonymous"} +load_mechs = {"plain", "digest-md5", "anonymous", "scram"} for _, mech in ipairs(load_mechs) do local name = "util.sasl."..mech; local m = require(name); diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index c3bc9600..a347e2f3 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -12,7 +12,8 @@ -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. local s_match = string.match; - +local type = type +local string = string local base64 = require "util.encodings".base64; local xor = require "bit".bxor local hmac_sha1 = require "util.hmac".sha1; @@ -34,7 +35,7 @@ local function bp( b ) end local function binaryXOR( a, b ) - if string.len(a) > string.len(b) then + if a:len() > b:len() then b = string.rep("\0", a:len() - b:len())..b elseif string.len(a) < string.len(b) then a = string.rep("\0", b:len() - a:len())..a @@ -60,8 +61,12 @@ end local function validate_username(username) -- check for forbidden char sequences - for eq in s:gmatch("=(.?.?)") do - if eq ~= "2D" and eq ~= "3D" then return false end end return true; + for eq in username:gmatch("=(.?.?)") do + if eq ~= "2D" and eq ~= "3D" then + return false + end + end + -- replace =2D with , and =3D with = -- apply SASLprep @@ -74,6 +79,7 @@ local function scram_sha_1(self, message) if not self.state.name then -- we are processing client_first_message local client_first_message = message; + self.state["client_first_message"] = client_first_message; self.state["name"] = client_first_message:match("n=(.+),r=") self.state["clientnonce"] = client_first_message:match("r=([^,]+)") @@ -85,8 +91,10 @@ local function scram_sha_1(self, message) self.state["salt"] = generate_uuid(); local server_first_message = "r="..self.state.clientnonce..self.state.servernonce..",s="..base64.encode(self.state.salt)..",i="..default_i; + self.state["server_first_message"] = server_first_message; return "challenge", server_first_message else + if type(message) ~= "string" then return "failure", "malformed-request" end -- we are processing client_final_message local client_final_message = message; @@ -108,13 +116,14 @@ local function scram_sha_1(self, message) local ClientKey = hmac_sha1(SaltedPassword, "Client Key") local ServerKey = hmac_sha1(SaltedPassword, "Server Key") local StoredKey = sha1(ClientKey) - local AuthMessage = "n=" .. s_match(client_first_message,"n=(.+)") .. "," .. server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") + local AuthMessage = "n=" .. s_match(self.state.client_first_message,"n=(.+)") .. "," .. self.state.server_first_message .. "," .. s_match(client_final_message, "(.+),p=.+") local ClientSignature = hmac_sha1(StoredKey, AuthMessage) local ClientProof = binaryXOR(ClientKey, ClientSignature) local ServerSignature = hmac_sha1(ServerKey, AuthMessage) if base64.encode(ClientProof) == self.state.proof then local server_final_message = "v="..base64.encode(ServerSignature); + self["username"] = self.state.name; return "success", server_final_message; else return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated."; -- cgit v1.2.3 From 7815630d28c0ffe23c63c54eeb4f401252b61d0f Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Wed, 18 Nov 2009 11:59:50 +0100 Subject: SASLprep usernames and passwords. --- util/sasl/scram.lua | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index a347e2f3..7d60ef86 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -19,6 +19,8 @@ local xor = require "bit".bxor local hmac_sha1 = require "util.hmac".sha1; local sha1 = require "util.hashes".sha1; local generate_uuid = require "util.uuid".generate; +local saslprep = require "util.encodings".stringprep.saslprep; +local log = require "util.logger".init("sasl"); module "plain" @@ -70,6 +72,7 @@ local function validate_username(username) -- replace =2D with , and =3D with = -- apply SASLprep + username = saslprep(username); return username; end @@ -83,10 +86,16 @@ local function scram_sha_1(self, message) self.state["name"] = client_first_message:match("n=(.+),r=") self.state["clientnonce"] = client_first_message:match("r=([^,]+)") - self.state.name = validate_username(self.state.name); if not self.state.name or not self.state.clientnonce then return "failure", "malformed-request"; end + + self.state.name = validate_username(self.state.name); + if not self.state.name then + log("debug", "Username violates either SASLprep or contains forbidden character sequences.") + return "failure", "malformed-request"; + end + self.state["servernonce"] = generate_uuid(); self.state["salt"] = generate_uuid(); @@ -110,6 +119,11 @@ local function scram_sha_1(self, message) password, state = self.profile.plain(self.state.name, self.realm) if state == nil then return "failure", "not-authorized" elseif state == false then return "failure", "account-disabled" end + password = saslprep(password); + if not password then + log("debug", "Password violates SASLprep."); + return "failure", "not-authorized" + end end local SaltedPassword = Hi(hmac_sha1, password, self.state.salt, default_i) -- cgit v1.2.3 From fd57560a965a70db6591a5847a12dc320c2cdf0f Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Wed, 18 Nov 2009 22:02:32 +0100 Subject: SASLprep authentication and password in SASL PLAIN implementation. --- util/sasl/plain.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 08056053..d9fdb9a2 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -12,6 +12,8 @@ -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. local s_match = string.match; +local saslprep = require "util.encodings".stringprep.saslprep; +local log = require "util.logger".init("sasl"); module "plain" @@ -26,6 +28,14 @@ local function plain(self, message) if authentication == nil or password == nil then return "failure", "malformed-request"; end + + -- SASLprep password and authentication + authentication = saslprep(authentication); + password = saslprep(password); + + if (not password) or (password == "") or (not authentication) or (authentication == "") then + log("debug", "Username or password violates either SASLprep."); + end local correct, state = false, false; if self.profile.plain then -- cgit v1.2.3 From 2519d3119c21701d6265bd1238becbdf30873909 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Wed, 18 Nov 2009 22:56:50 +0100 Subject: Enable restriction of supported mechanisms in the SASL library. --- util/sasl.lua | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index 82fc1226..9df74c1b 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -16,6 +16,8 @@ local md5 = require "util.hashes".md5; local log = require "util.logger".init("sasl"); local tostring = tostring; local st = require "util.stanza"; +local set = require "util.set"; +local array = require "util.array"; local pairs, ipairs = pairs, ipairs; local t_insert, t_concat = table.insert, table.concat; local to_unicode = require "util.encodings".idna.to_unicode; @@ -84,20 +86,34 @@ local function registerMechanism(name, backends, f) end -- create a new SASL object which can be used to authenticate clients -function new(realm, profile) +function new(realm, profile, forbidden) sasl_i = {profile = profile}; sasl_i.realm = realm; - return setmetatable(sasl_i, method); + s = setmetatable(sasl_i, method); + s:forbidden(sasl_i, forbidden) + return s; +end + +-- set the forbidden mechanisms +function method:forbidden( forbidden ) + if forbidden then + -- set forbidden + self.forbidden = set.new(forbidden); + else + -- get forbidden + return array.collect(self.forbidden:items()); + end end -- get a list of possible SASL mechanims to use function method:mechanisms() local mechanisms = {} for backend, f in pairs(self.profile) do - print(backend) if backend_mechanism[backend] then for _, mechanism in ipairs(backend_mechanism[backend]) do - mechanisms[mechanism] = true; + if not sasl_i.forbidden:contains(mechanism) then + mechanisms[mechanism] = true; + end end end end -- cgit v1.2.3 From 517d02616edec5a7b3b2ca5de3040beb6a383a73 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Wed, 18 Nov 2009 22:59:43 +0100 Subject: Tidying up. --- util/sasl.lua | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index 9df74c1b..6f650a5c 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -14,23 +14,19 @@ local md5 = require "util.hashes".md5; local log = require "util.logger".init("sasl"); -local tostring = tostring; local st = require "util.stanza"; local set = require "util.set"; local array = require "util.array"; +local to_unicode = require "util.encodings".idna.to_unicode; + +local tostring = tostring; local pairs, ipairs = pairs, ipairs; local t_insert, t_concat = table.insert, table.concat; -local to_unicode = require "util.encodings".idna.to_unicode; local s_match = string.match; -local gmatch = string.gmatch -local string = string -local math = require "math" local type = type local error = error -local print = print local setmetatable = setmetatable; local assert = assert; -local dofile = dofile; local require = require; require "util.iterators" -- cgit v1.2.3 From 406173262fb1ecc313db90d11134f2e5b50bd2d4 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Wed, 18 Nov 2009 23:25:27 +0100 Subject: Cleaning up. --- util/sasl.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index 6f650a5c..7b7db024 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -91,13 +91,13 @@ function new(realm, profile, forbidden) end -- set the forbidden mechanisms -function method:forbidden( forbidden ) - if forbidden then +function method:forbidden( restrict ) + if restrict then -- set forbidden - self.forbidden = set.new(forbidden); + self.restrict = set.new(restrict); else -- get forbidden - return array.collect(self.forbidden:items()); + return array.collect(self.restrict:items()); end end @@ -107,7 +107,7 @@ function method:mechanisms() for backend, f in pairs(self.profile) do if backend_mechanism[backend] then for _, mechanism in ipairs(backend_mechanism[backend]) do - if not sasl_i.forbidden:contains(mechanism) then + if not sasl_i.restrict:contains(mechanism) then mechanisms[mechanism] = true; end end -- cgit v1.2.3 From 74de4e38040d61d111b875a65bcb705a7047ef77 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Wed, 18 Nov 2009 23:26:35 +0100 Subject: Provide SASL PLAIN mechanism only if TLS is active. --- plugins/mod_saslauth.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 641b08f0..d595fd24 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -141,9 +141,11 @@ module:add_event_hook("stream-features", session.sasl_handler = new_sasl(session.host, anonymous_authentication_profile); else session.sasl_handler = new_sasl(session.host, default_authentication_profile); + if not session.secure then + session.sasl_handler:forbidden({"PLAIN"}); + end end features:tag("mechanisms", mechanisms_attr); - -- TODO: Provide PLAIN only if TLS is active, this is a SHOULD from the introduction of RFC 4616. This behavior could be overridden via configuration but will issuing a warning or so. for k, v in pairs(session.sasl_handler:mechanisms()) do features:tag("mechanism"):text(v):up(); end -- cgit v1.2.3 From 6117baa28d8658b16a4de88f4183bef42bed8e0a Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 19 Nov 2009 00:04:14 +0100 Subject: Handle , and = in usernames for SCRAM. --- util/sasl/scram.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 7d60ef86..a7b21d0d 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -70,6 +70,8 @@ local function validate_username(username) end -- replace =2D with , and =3D with = + username:gsub("=2D", ","); + username:gsub("=3D", "="); -- apply SASLprep username = saslprep(username); -- cgit v1.2.3 From d5511aa898fea7f142f3b61799ea0724b4b8877f Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 19 Nov 2009 15:29:09 +0100 Subject: Typo. --- util/sasl/digest-md5.lua | 2 +- util/sasl/scram.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index a4a4f811..1429a5c6 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -24,7 +24,7 @@ local md5 = require "util.hashes".md5; local log = require "util.logger".init("sasl"); local generate_uuid = require "util.uuid".generate; -module "plain" +module "digest-md5" --========================= --SASL DIGEST-MD5 according to RFC 2831 diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index a7b21d0d..f7b8300a 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -22,7 +22,7 @@ local generate_uuid = require "util.uuid".generate; local saslprep = require "util.encodings".stringprep.saslprep; local log = require "util.logger".init("sasl"); -module "plain" +module "scram" --========================= --SASL SCRAM-SHA-1 according to draft-ietf-sasl-scram-10 -- cgit v1.2.3 From 5ee762728b5acc38906ae3ba5523ded1f0d7005c Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 19 Nov 2009 16:43:38 +0100 Subject: Allow SASL PLAIN over unsecure connections when intended by admin. --- plugins/mod_saslauth.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index d595fd24..2223f056 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -141,7 +141,7 @@ module:add_event_hook("stream-features", session.sasl_handler = new_sasl(session.host, anonymous_authentication_profile); else session.sasl_handler = new_sasl(session.host, default_authentication_profile); - if not session.secure then + if not (module:get_option("allow_unencrypted_plain_auth")) and not session.secure then session.sasl_handler:forbidden({"PLAIN"}); end end -- cgit v1.2.3 From c52c4021b0c8e890bb7334fdbf00b9550b17e21f Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 19 Nov 2009 16:44:37 +0100 Subject: Use new cofig option reading API. --- plugins/mod_saslauth.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 2223f056..04e33b29 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -137,7 +137,7 @@ module:add_event_hook("stream-features", if secure_auth_only and not session.secure then return; end - if config.get(session.host or "*", "core", "anonymous_login") then + if module:get_option("anonymous_login") then session.sasl_handler = new_sasl(session.host, anonymous_authentication_profile); else session.sasl_handler = new_sasl(session.host, default_authentication_profile); -- cgit v1.2.3 From 547b733736f76dac6fce0757ec73742377f5f99b Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 19 Nov 2009 17:08:58 +0100 Subject: Fail if username or password don't pass SASLprep. --- util/sasl/plain.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index d9fdb9a2..5c7ff68a 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -34,7 +34,8 @@ local function plain(self, message) password = saslprep(password); if (not password) or (password == "") or (not authentication) or (authentication == "") then - log("debug", "Username or password violates either SASLprep."); + log("debug", "Username or password violates SASLprep."); + return "failure", "malformed-request"; end local correct, state = false, false; -- cgit v1.2.3 From b32b0e118daa6a32b464bf21e7c091041eec0dbb Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 19 Nov 2009 17:17:52 +0100 Subject: Adding some human readable error messages. --- util/sasl/plain.lua | 4 ++-- util/sasl/scram.lua | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 5c7ff68a..46a86bb9 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -35,7 +35,7 @@ local function plain(self, message) if (not password) or (password == "") or (not authentication) or (authentication == "") then log("debug", "Username or password violates SASLprep."); - return "failure", "malformed-request"; + return "failure", "malformed-request", "Invalid username or password."; end local correct, state = false, false; @@ -55,7 +55,7 @@ local function plain(self, message) if correct then return "success"; else - return "failure", "not-authorized"; + return "failure", "not-authorized", "Unable to authorize you with the authentication credentials you've sent."; end end diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index f7b8300a..4413e2a6 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -95,7 +95,7 @@ local function scram_sha_1(self, message) self.state.name = validate_username(self.state.name); if not self.state.name then log("debug", "Username violates either SASLprep or contains forbidden character sequences.") - return "failure", "malformed-request"; + return "failure", "malformed-request", "Invalid username."; end self.state["servernonce"] = generate_uuid(); @@ -113,7 +113,7 @@ local function scram_sha_1(self, message) self.state["nonce"] = client_final_message:match("r=(.+),p="); self.state["channelbinding"] = client_final_message:match("c=(.+),r="); if not self.state.proof or not self.state.nonce or not self.state.channelbinding then - return "failure", "malformed-request"; + return "failure", "malformed-request", "Missing an attribute(p, r or c) in SASL message."; end local password; @@ -124,7 +124,7 @@ local function scram_sha_1(self, message) password = saslprep(password); if not password then log("debug", "Password violates SASLprep."); - return "failure", "not-authorized" + return "failure", "not-authorized", "Invalid password." end end -- cgit v1.2.3 From 315e7ac799abb96f627be4c5b27648f007b89be7 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 19 Nov 2009 17:20:38 +0100 Subject: Reduce needed roundtrips during DIGEST-MD5 login. --- util/sasl/digest-md5.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index 1429a5c6..e80ed63a 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -214,8 +214,7 @@ local function digest(self, message) KD = HA1..":"..response["nonce"]..":"..response["nc"]..":"..response["cnonce"]..":"..response["qop"]..":"..HA2 local rspauth = md5(KD, true); self.authenticated = true; - --TODO: considering sending the rspauth in a success node for saving one roundtrip; allowed according to http://tools.ietf.org/html/draft-saintandre-rfc3920bis-09#section-7.3.6 - return "challenge", serialize({rspauth = rspauth}); + return "success", serialize({rspauth = rspauth}); else return "failure", "not-authorized", "The response provided by the client doesn't match the one we calculated." end -- cgit v1.2.3 From b149c783f2753da791919b278ed19a3b1a79f212 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 20 Nov 2009 22:58:56 +0000 Subject: net.server_event: Initial commit of server_event.lua. Don't get too excited, it's not used at all yet, and is still incomplete :) --- net/server_event.lua | 759 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 759 insertions(+) create mode 100644 net/server_event.lua diff --git a/net/server_event.lua b/net/server_event.lua new file mode 100644 index 00000000..15d1dea0 --- /dev/null +++ b/net/server_event.lua @@ -0,0 +1,759 @@ +--[[ + + + server.lua based on lua/libevent by blastbeat + + notes: + -- when using luaevent, never register 2 or more EV_READ at one socket, same for EV_WRITE + -- you cant even register a new EV_READ/EV_WRITE callback inside another one + -- never call eventcallback:close( ) from inside eventcallback + -- to do some of the above, use timeout events or something what will called from outside + -- dont let garbagecollect eventcallbacks, as long they are running + -- when using luasec, there are 4 cases of timeout errors: wantread or wantwrite during reading or writing + +--]] + + +local SCRIPT_NAME = "server_event.lua" +local SCRIPT_VERSION = "0.05" +local SCRIPT_AUTHOR = "blastbeat" +local LAST_MODIFIED = "2009/11/20" + +local cfg = { + MAX_CONNECTIONS = 100000, -- max per server connections (use "ulimit -n" on *nix) + MAX_HANDSHAKE_ATTEMPS = 10, -- attemps to finish ssl handshake + HANDSHAKE_TIMEOUT = 1, -- timout in seconds per handshake attemp + MAX_READ_LENGTH = 1024 * 1024 * 1024 * 1024, -- max bytes allowed to read from sockets + MAX_SEND_LENGTH = 1024 * 1024 * 1024 * 1024, -- max bytes size of write buffer (for writing on sockets) + ACCEPT_DELAY = 10, -- seconds to wait until the next attemp of a full server to accept + READ_TIMEOUT = 60 * 30, -- timeout in seconds for read data from socket + WRITE_TIMEOUT = 30, -- timeout in seconds for write data on socket + CONNECT_TIMEOUT = 10, -- timeout in seconds for connection attemps + CLEAR_DELAY = 5, -- seconds to wait for clearing interface list (and calling ondisconnect listeners) + DEBUG = true, -- show debug messages +} + +local function use(x) return rawget(_G, x); end +local print = use "print" +local pcall = use "pcall" +local ipairs = use "ipairs" +local string = use "string" +local select = use "select" +local require = use "require" +local tostring = use "tostring" +local coroutine = use "coroutine" +local setmetatable = use "setmetatable" + +local ssl = use "ssl" +local socket = use "socket" + +local log = require ("util.logger").init("socket") + +local function debug(...) + return log("debug", ("%s "):rep(select('#', ...)), ...) +end + +local bitor = ( function( ) -- thx Rici Lake + local hasbit = function( x, p ) + return x % ( p + p ) >= p + end + return function( x, y ) + local p = 1 + local z = 0 + local limit = x > y and x or y + while p <= limit do + if hasbit( x, p ) or hasbit( y, p ) then + z = z + p + end + p = p + p + end + return z + end +end )( ) + +local getid = function( ) + return function( ) + end +end + +local event = require "luaevent.core" +local base = event.new( ) +local EV_READ = event.EV_READ +local EV_WRITE = event.EV_WRITE +local EV_TIMEOUT = event.EV_TIMEOUT + +local EV_READWRITE = bitor( EV_READ, EV_WRITE ) + +local interfacelist = ( function( ) -- holds the interfaces for sockets + local array = { } + local len = 0 + return function( method, arg ) + if "add" == method then + len = len + 1 + array[ len ] = arg + arg:_position( len ) + return len + elseif "delete" == method then + if len <= 0 then + return nil, "array is already empty" + end + local position = arg:_position() -- get position in array + if position ~= len then + local interface = array[ len ] -- get last interface + array[ position ] = interface -- copy it into free position + array[ len ] = nil -- free last position + interface:_position( position ) -- set new position in array + else -- free last position + array[ len ] = nil + end + len = len - 1 + return len + else + return array + end + end +end )( ) + +-- Client interface methods +local interface_mt +do + interface_mt = {}; interface_mt.__index = interface_mt; + + local addevent = base.addevent + local coroutine_wrap, coroutine_yield = coroutine.wrap,coroutine.yield + local string_len = string.len + + -- Private methods + function interface_mt:_position(new_position) + self.position = new_position or self.position + return self.position; + end + function interface_mt:_close() -- regs event to start self:_destroy() + local callback = function( ) + self:_destroy(); + self.eventclose = nil + return -1 + end + self.eventclose = addevent( base, nil, EV_TIMEOUT, callback, 0 ) + return true + end + + function interface_mt:_start_connection(plainssl) -- should be called from addclient + local callback = function( event ) + if EV_TIMEOUT == event then -- timout during connection + self.fatalerror = "connection timeout" + self.listener.ontimeout( self ) -- call timeout listener + self:_close() + debug( "new connection failed. id:", self, "error:", self.fatalerror ) + else + if plainssl then -- start ssl session + self:_start_ssl( self.listener.onconnect ) + else -- normal connection + self:_start_session( self.listener.onconnect ) + end + debug( "new connection established. id:", self ) + end + self.eventconnect = nil + return -1 + end + self.eventconnect = addevent( base, self.conn, EV_WRITE, callback, cfg.CONNECT_TIMEOUT ) + return true + end + function interface_mt:_start_session(onconnect) -- new session, for example after startssl + if self.type == "client" then + local callback = function( ) + self:_lock( false, false, false ) + --vdebug( "start listening on client socket with id:", self ) + self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT ) -- register callback + onconnect( self ) + self.eventsession = nil + return -1 + end + self.eventsession = addevent( base, nil, EV_TIMEOUT, callback, 0 ) + else + self:_lock( false ) + --vdebug( "start listening on server socket with id:", self ) + self.eventread = addevent( base, self.conn, EV_READ, self.readcallback ) -- register callback + end + return true + end + function interface_mt:_start_ssl(arg) -- old socket will be destroyed, therefore we have to close read/write events first + --vdebug( "starting ssl session with client id:", self ) + local _ + _ = self.eventread and self.eventread:close( ) -- close events; this must be called outside of the event callbacks! + _ = self.eventwrite and self.eventwrite:close( ) + self.eventread, self.eventwrite = nil, nil + local err + self.conn, err = ssl.wrap( self.conn, self.sslctx ) + if err then + self.fatalerror = err + self.conn = nil -- cannot be used anymore + if "onconnect" == arg then + self.ondisconnect = nil -- dont call this when client isnt really connected + end + self:_close() + debug( "fatal error while ssl wrapping:", err ) + return false + end + self.conn:settimeout( 0 ) -- set non blocking + local handshakecallback = coroutine_wrap( + function( event ) + local _, err + local attempt = 0 + local maxattempt = cfg.MAX_HANDSHAKE_ATTEMPS + while attempt < 1000 do -- no endless loop + attempt = attempt + 1 + debug( "ssl handshake of client with id:", self, "attemp:", attempt ) + if attempt > maxattempt then + self.fatalerror = "max handshake attemps exceeded" + elseif EV_TIMEOUT == event then + self.fatalerror = "timeout during handshake" + else + _, err = self.conn:dohandshake( ) + if not err then + self:_lock( false, false, false ) -- unlock the interface; sending, closing etc allowed + self.send = self.conn.send -- caching table lookups with new client object + self.receive = self.conn.receive + local onsomething + if "onconnect" == arg then -- trigger listener + onsomething = self.listener.onconnect + else + onsomething = self.listener.onsslconnection + end + self:_start_session( onsomething ) + debug( "ssl handshake done" ) + self.eventhandshake = nil + return -1 + end + debug( "error during ssl handshake:", err ) + if err == "wantwrite" then + event = EV_WRITE + elseif err == "wantread" then + event = EV_READ + else + self.fatalerror = err + end + end + if self.fatalerror then + if "onconnect" == arg then + self.ondisconnect = nil -- dont call this when client isnt really connected + end + self:_close() + debug( "handshake failed because:", self.fatalerror ) + self.eventhandshake = nil + return -1 + end + event = coroutine_yield( event, cfg.HANDSHAKE_TIMEOUT ) -- yield this monster... + end + end + ) + debug "starting handshake..." + self:_lock( false, true, true ) -- unlock read/write events, but keep interface locked + self.eventhandshake = addevent( base, self.conn, EV_READWRITE, handshakecallback, cfg.HANDSHAKE_TIMEOUT ) + return true + end + function interface_mt:_destroy() -- close this interface + events and call last listener + debug( "closing client with id:", self ) + self:_lock( true, true, true ) -- first of all, lock the interface to avoid further actions + local _ + _ = self.eventread and self.eventread:close( ) -- close events; this must be called outside of the event callbacks! + if self.type == "client" then + _ = self.eventwrite and self.eventwrite:close( ) + _ = self.eventhandshake and self.eventhandshake:close( ) + _ = self.eventstarthandshake and self.eventstarthandshake:close( ) + _ = self.eventconnect and self.eventconnect:close( ) + _ = self.eventsession and self.eventsession:close( ) + _ = self.eventwritetimeout and self.eventwritetimeout:close( ) + _ = self.eventreadtimeout and self.eventreadtimeout:close( ) + _ = self.ondisconnect and self:ondisconnect( self.fatalerror ) -- call ondisconnect listener (wont be the case if handshake failed on connect) + _ = self.conn and self.conn:close( ) -- close connection, must also be called outside of any socket registered events! + self._server:counter(-1); + self.eventread, self.eventwrite = nil, nil + self.eventstarthandshake, self.eventhandshake, self.eventclose = nil, nil, nil + self.readcallback, self.writecallback = nil, nil + else + self.conn:close( ) + self.eventread, self.eventclose = nil, nil + self.interface, self.readcallback = nil, nil + end + interfacelist( "delete", self ) + return true + end + function interface_mt:_lock(nointerface, noreading, nowriting) -- lock or unlock this interface or events + self.nointerface, self.noreading, self.nowriting = nointerface, noreading, nowriting + return nointerface, noreading, nowriting + end + + function interface_mt:counter(c) + if c then + self._connections = self._connections - c + end + return self._connections + end + + -- Public methods + function interface_mt:write(data) + --vdebug( "try to send data to client, id/data:", self, data ) + data = tostring( data ) + local len = string_len( data ) + local total = len + self.writebufferlen + if total > cfg.MAX_SEND_LENGTH then -- check buffer length + local err = "send buffer exceeded" + debug( "error:", err ) -- to much, check your app + return nil, err + end + self.writebuffer = self.writebuffer .. data -- new buffer + self.writebufferlen = total + if not self.eventwrite then -- register new write event + --vdebug( "register new write event" ) + self.eventwrite = addevent( base, self.conn, EV_WRITE, self.writecallback, cfg.WRITE_TIMEOUT ) + end + return true + end + function interface_mt:close(now) + debug( "try to close client connection with id:", self ) + if self.type == "client" then + self.fatalerror = "client to close" + if ( not self.eventwrite ) or now then -- try to close immediately + self:_lock( true, true, true ) + self:_close() + return true + else -- wait for incomplete write request + self:_lock( true, true, false ) + debug "closing delayed until writebuffer is empty" + return nil, "writebuffer not empty, waiting" + end + else + debug( "try to close server with id:", self, "args:", now ) + self.fatalerror = "server to close" + self:_lock( true ) + local count = 0 + for _, item in ipairs( interfacelist( ) ) do + if ( item.type ~= "server" ) and ( item._server == self ) then -- client/server match + if item:close( now ) then -- writebuffer was empty + count = count + 1 + end + end + end + local timeout = 0 -- dont wait for unfinished writebuffers of clients... + if not now then + timeout = cfg.WRITE_TIMEOUT -- ...or wait for it + end + self:_close( timeout ) -- add new event to remove the server interface + debug( "seconds remained until server is closed:", timeout ) + return count -- returns finished clients with empty writebuffer + end + end + + function interface_mt:server() + return self._server or self; + end + + function interface_mt:port() + return self._port + end + + function interface_mt:ip() + return self._ip + end + + function interface_mt:ssl() + return self.usingssl + end + + function interface_mt:type() + return self._type or "client" + end + + function interface_mt:connections() + return self._connections + end + + function interface_mt:address() + return self.addr + end + + + + function interface_mt:starttls() + debug( "try to start ssl at client id:", self ) + local err + if not self.sslctx then -- no ssl available + err = "no ssl context available" + elseif self.usingssl then -- startssl was already called + err = "ssl already active" + end + if err then + debug( "error:", err ) + return nil, err + end + self.usingssl = true + self.startsslcallback = function( ) -- we have to start the handshake outside of a read/write event + self:_start_ssl(); + self.eventstarthandshake = nil + return -1 + end + if not self.eventwrite then + self:_lock( true, true, true ) -- lock the interface, to not disturb the handshake + self.eventstarthandshake = addevent( base, nil, EV_TIMEOUT, self.startsslcallback, 0 ) -- add event to start handshake + else -- wait until writebuffer is empty + self:_lock( true, true, false ) + debug "ssl session delayed until writebuffer is empty..." + end + return true + end + + function interface_mt.onconnect() + end +end + +-- End of client interface methods + +local handleclient; +do + local string_sub = string.sub -- caching table lookups + local string_len = string.len + local addevent = base.addevent + local coroutine_wrap = coroutine.wrap + local socket_gettime = socket.gettime + local coroutine_yield = coroutine.yield + function handleclient( client, ip, port, server, pattern, listener, _, sslctx ) -- creates an client interface + --vdebug("creating client interfacce...") + local interface = { + type = "client"; + conn = client; + currenttime = socket_gettime( ); -- safe the origin + writebuffer = ""; -- writebuffer + writebufferlen = 0; -- length of writebuffer + send = client.send; -- caching table lookups + receive = client.receive; + onconnect = listener.onconnect; -- will be called when client disconnects + ondisconnect = listener.ondisconnect; -- will be called when client disconnects + onincoming = listener.onincoming; -- will be called when client sends data + eventread = false, eventwrite = false, eventclose = false, + eventhandshake = false, eventstarthandshake = false; -- event handler + eventconnect = false, eventsession = false; -- more event handler... + eventwritetimeout = false; -- even more event handler... + eventreadtimeout = false; + fatalerror = false; -- error message + writecallback = false; -- will be called on write events + readcallback = false; -- will be called on read events + nointerface = true; -- lock/unlock parameter of this interface + noreading = false, nowriting = false; -- locks of the read/writecallback + startsslcallback = false; -- starting handshake callback + position = false; -- position of client in interfacelist + + -- Properties + _ip = ip, _port = port, _server = server, _pattern = pattern, + _sslctx = sslctx; -- parameters + _usingssl = false; -- client is using ssl; + } + interface.writecallback = function( event ) -- called on write events + --vdebug( "new client write event, id/ip/port:", interface, ip, port ) + if interface.nowriting or ( interface.fatalerror and ( "client to close" ~= interface.fatalerror ) ) then -- leave this event + --vdebug( "leaving this event because:", interface.nowriting or interface.fatalerror ) + interface.eventwrite = false + return -1 + end + if EV_TIMEOUT == event then -- took too long to write some data to socket -> disconnect + interface.fatalerror = "timeout during writing" + debug( "writing failed:", interface.fatalerror ) + interface:_close() + interface.eventwrite = false + return -1 + else -- can write :) + if interface.usingssl then -- handle luasec + if interface.eventreadtimeout then -- we have to read first + local ret = interface.readcallback( ) -- call readcallback + --vdebug( "tried to read in writecallback, result:", ret ) + end + if interface.eventwritetimeout then -- luasec only + interface.eventwritetimeout:close( ) -- first we have to close timeout event which where regged after a wantread error + interface.eventwritetimeout = false + end + end + local succ, err, byte = interface.send( interface.conn, interface.writebuffer, 1, interface.writebufferlen ) + --vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte ) + if succ then -- writing succesful + interface.writebuffer = "" + interface.writebufferlen = 0 + if interface.fatalerror then + debug "closing client after writing" + interface:_close() -- close interface if needed + elseif interface.startsslcallback then -- start ssl connection if needed + debug "starting ssl handshake after writing" + interface.eventstarthandshake = addevent( base, nil, EV_TIMEOUT, interface.startsslcallback, 0 ) + elseif interface.eventreadtimeout then + return EV_WRITE, EV_TIMEOUT + end + interface.eventwrite = nil + return -1 + elseif byte then -- want write again + --vdebug( "writebuffer is not empty:", err ) + interface.writebuffer = string_sub( interface.writebuffer, byte + 1, interface.writebufferlen ) -- new buffer + interface.writebufferlen = interface.writebufferlen - byte + if "wantread" == err then -- happens only with luasec + local callback = function( ) + interface:_close() + interface.eventwritetimeout = nil + return evreturn, evtimeout + end + interface.eventwritetimeout = addevent( base, nil, EV_TIMEOUT, callback, cfg.WRITE_TIMEOUT ) -- reg a new timeout event + debug( "wantread during write attemp, reg it in readcallback but dont know what really happens next..." ) + -- hopefully this works with luasec; its simply not possible to use 2 different write events on a socket in luaevent + return -1 + end + return EV_WRITE, cfg.WRITE_TIMEOUT + else -- connection was closed during writing or fatal error + interface.fatalerror = err or "fatal error" + debug( "connection failed in write event:", interface.fatalerror ) + interface:_close() + interface.eventwrite = nil + return -1 + end + end + end + local usingssl, receive = interface._usingssl, interface.receive; + interface.readcallback = function( event ) -- called on read events + --vdebug( "new client read event, id/ip/port:", interface, ip, port ) + if interface.noreading or interface.fatalerror then -- leave this event + --vdebug( "leaving this event because:", interface.noreading or interface.fatalerror ) + interface.eventread = nil + return -1 + end + if EV_TIMEOUT == event then -- took too long to get some data from client -> disconnect + interface.fatalerror = "timeout during receiving" + debug( "connection failed:", interface.fatalerror ) + interface:_close() + interface.eventread = nil + return -1 + else -- can read + if usingssl then -- handle luasec + if interface.eventwritetimeout then -- ok, in the past writecallback was regged + local ret = interface.writecallback( ) -- call it + --vdebug( "tried to write in readcallback, result:", ret ) + end + if interface.eventreadtimeout then + interface.eventreadtimeout:close( ) + interface.eventreadtimeout = nil + end + end + local buffer, err, part = receive( client, pattern ) -- receive buffer with "pattern" + --vdebug( "read data:", buffer, "error:", err, "part:", part ) + buffer = buffer or part or "" + local len = string_len( buffer ) + if len > cfg.MAX_READ_LENGTH then -- check buffer length + interface.fatalerror = "receive buffer exceeded" + debug( "fatal error:", interface.fatalerror ) + interface:_close() + interface.eventread = nil + return -1 + end + if err and ( "timeout" ~= err ) then + if "wantwrite" == err then -- need to read on write event + if not interface.eventwrite then -- register new write event if needed + interface.eventwrite = addevent( base, interface.conn, EV_WRITE, interface.writecallback, cfg.WRITE_TIMEOUT ) + end + interface.eventreadtimeout = addevent( base, nil, EV_TIMEOUT, + function( ) + interface:_close() + end, cfg.READ_TIMEOUT + ) + debug( "wantwrite during read attemp, reg it in writecallback but dont know what really happens next..." ) + -- to be honest i dont know what happens next, if it is allowed to first read, the write etc... + else -- connection was closed or fatal error + interface.fatalerror = err + debug( "connection failed in read event:", interface.fatalerror ) + interface:_close() + interface.eventread = nil + return -1 + end + end + interface.onincoming( interface, buffer, err ) -- send new data to listener + return EV_READ, cfg.READ_TIMEOUT + end + end + + client:settimeout( 0 ) -- set non blocking + setmetatable(interface, interface_mt) + interfacelist( "add", interface ) -- add to interfacelist + return interface + end +end + +local handleserver +do + function handleserver( server, addr, port, pattern, listener, sslctx, startssl ) -- creates an server interface + debug "creating server interface..." + local interface = { + _connections = 0; + + conn = server; + onconnect = listener.onconnect; -- will be called when new client connected + eventread = false; -- read event handler + eventclose = false; -- close event handler + readcallback = false; -- read event callback + fatalerror = false; -- error message + nointerface = true; -- lock/unlock parameter + } + interface.readcallback = function( event ) -- server handler, called on incoming connections + --vdebug( "server can accept, id/addr/port:", interface, addr, port ) + if interface.fatalerror then + --vdebug( "leaving this event because:", self.fatalerror ) + interface.eventread = nil + return -1 + end + local delay = cfg.ACCEPT_DELAY + if EV_TIMEOUT == event then + if interface._connections >= cfg.MAX_CONNECTIONS then -- check connection count + debug( "to many connections, seconds to wait for next accept:", delay ) + return EV_TIMEOUT, delay -- timeout... + else + return EV_READ -- accept again + end + end + --vdebug("max connection check ok, accepting...") + local client, err = server:accept() -- try to accept; TODO: check err + while client do + if interface._connections >= cfg.MAX_CONNECTIONS then + client:close( ) -- refuse connection + debug( "maximal connections reached, refuse client connection; accept delay:", delay ) + return EV_TIMEOUT, delay -- delay for next accept attemp + end + local ip, port = client:getpeername( ) + interface._connections = interface._connections + 1 -- increase connection count + local clientinterface = handleclient( client, ip, port, interface, pattern, listener, nil, sslctx ) + --vdebug( "client id:", clientinterface, "startssl:", startssl ) + if startssl then + clientinterface:_start_ssl( clientinterface.onconnect ) + else + clientinterface:_start_session( clientinterface.onconnect ) + end + debug( "accepted incoming client connection from:", ip, port ) + client, err = server:accept() -- try to accept again + end + return EV_READ + end + + server:settimeout( 0 ) + setmetatable(interface, interface_mt) + interfacelist( "add", interface ) + interface:_start_session() + return interface + end +end + +local addserver = ( function( ) + return function( addr, port, listener, pattern, backlog, sslcfg, startssl ) -- TODO: check arguments + debug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslcfg or "nil", startssl or "nil") + local server, err = socket.bind( addr, port, backlog ) -- create server socket + if not server then + debug( "creating server socket failed because:", err ) + return nil, err + end + local sslctx + if sslcfg then + if not ssl then + debug "fatal error: luasec not found" + return nil, "luasec not found" + end + sslctx, err = ssl.newcontext( sslcfg ) + if err then + debug( "error while creating new ssl context for server socket:", err ) + return nil, err + end + end + local interface = handleserver( server, addr, port, pattern, listener, sslctx, startssl ) -- new server handler + debug( "new server created with id:", tostring(interface)) + return interface + end +end )( ) + +local wrapclient = ( function( ) + return function( client, addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) + debug( "try to connect to:", addr, serverport, "with parameters:", pattern, localaddr, localport, sslcfg, startssl ) + local sslctx + if sslcfg then -- handle ssl/new context + if not ssl then + debug "need luasec, but not available" + return nil, "luasec not found" + end + sslctx, err = ssl.newcontext( sslcfg ) + if err then + debug( "cannot create new ssl context:", err ) + return nil, err + end + end + end +end )( ) + +local addclient = ( function( ) + return function( addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) + local client, err = socket.tcp() -- creating new socket + if not client then + debug( "cannot create socket:", err ) + return nil, err + end + client:settimeout( 0 ) -- set nonblocking + if localaddr then + local res, err = client:bind( localaddr, localport, -1 ) + if not res then + debug( "cannot bind client:", err ) + return nil, err + end + end + local res, err = client:connect( addr, serverport ) -- connect + if res or ( err == "timeout" ) then + local ip, port = client:getsockname( ) + local server = function( ) + return nil, "this is a dummy server interface" + end + local interface = handleclient( client, ip, port, server, pattern, listener, sslctx ) + interface:_start_connection( startssl ) + debug( "new connection id:", interface ) + return interface, err + else + debug( "new connection failed:", err ) + return nil, err + end + return wrapclient( client, addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) + end +end )( ) + +local loop = function( ) -- starts the event loop + return base:loop( ) +end + +local newevent = ( function( ) + local add = base.addevent + return function( ... ) + return add( base, ... ) + end +end )( ) + +local closeallservers = function( arg ) + for _, item in ipairs( interfacelist( ) ) do + if item "type" == "server" then + item( "close", arg ) + end + end +end + +return { + + cfg = cfg, + base = base, + loop = loop, + event = event, + addevent = newevent, + addserver = addserver, + addclient = addclient, + wrapclient = wrapclient, + closeallservers = closeallservers, + + __NAME = SCRIPT_NAME, + __DATE = LAST_MODIFIED, + __AUTHOR = SCRIPT_AUTHOR, + __VERSION = SCRIPT_VERSION, + +} -- cgit v1.2.3 From 83334e87456fa517e30f683f5c0948b8c18b6c83 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 20 Nov 2009 23:24:41 +0000 Subject: net.server_event: Remove redundant getid() function --- net/server_event.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index 15d1dea0..fab617b7 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -71,11 +71,6 @@ local bitor = ( function( ) -- thx Rici Lake end end )( ) -local getid = function( ) - return function( ) - end -end - local event = require "luaevent.core" local base = event.new( ) local EV_READ = event.EV_READ -- cgit v1.2.3 From 7f86cfbcc015218d36347be850be0849f55413fc Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 02:36:35 +0000 Subject: net.server: Rename to net.server_select --- net/server.lua | 914 -------------------------------------------------- net/server_select.lua | 914 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 914 insertions(+), 914 deletions(-) delete mode 100644 net/server.lua create mode 100644 net/server_select.lua diff --git a/net/server.lua b/net/server.lua deleted file mode 100644 index 6ab8ce91..00000000 --- a/net/server.lua +++ /dev/null @@ -1,914 +0,0 @@ --- --- server.lua by blastbeat of the luadch project --- Re-used here under the MIT/X Consortium License --- --- Modifications (C) 2008-2009 Matthew Wild, Waqas Hussain --- - --- // wrapping luadch stuff // -- - -local use = function( what ) - return _G[ what ] -end -local clean = function( tbl ) - for i, k in pairs( tbl ) do - tbl[ i ] = nil - end -end - -local log, table_concat = require ("util.logger").init("socket"), table.concat; -local out_put = function (...) return log("debug", table_concat{...}); end -local out_error = function (...) return log("warn", table_concat{...}); end -local mem_free = collectgarbage - -----------------------------------// DECLARATION //-- - ---// constants //-- - -local STAT_UNIT = 1 -- byte - ---// lua functions //-- - -local type = use "type" -local pairs = use "pairs" -local ipairs = use "ipairs" -local tostring = use "tostring" -local collectgarbage = use "collectgarbage" - ---// lua libs //-- - -local os = use "os" -local table = use "table" -local string = use "string" -local coroutine = use "coroutine" - ---// lua lib methods //-- - -local os_time = os.time -local os_difftime = os.difftime -local table_concat = table.concat -local table_remove = table.remove -local string_len = string.len -local string_sub = string.sub -local coroutine_wrap = coroutine.wrap -local coroutine_yield = coroutine.yield - ---// extern libs //-- - -local luasec = select( 2, pcall( require, "ssl" ) ) -local luasocket = require "socket" - ---// extern lib methods //-- - -local ssl_wrap = ( luasec and luasec.wrap ) -local socket_bind = luasocket.bind -local socket_sleep = luasocket.sleep -local socket_select = luasocket.select -local ssl_newcontext = ( luasec and luasec.newcontext ) - ---// functions //-- - -local id -local loop -local stats -local idfalse -local addtimer -local closeall -local addserver -local getserver -local wrapserver -local getsettings -local closesocket -local removesocket -local removeserver -local changetimeout -local wrapconnection -local changesettings - ---// tables //-- - -local _server -local _readlist -local _timerlist -local _sendlist -local _socketlist -local _closelist -local _readtimes -local _writetimes - ---// simple data types //-- - -local _ -local _readlistlen -local _sendlistlen -local _timerlistlen - -local _sendtraffic -local _readtraffic - -local _selecttimeout -local _sleeptime - -local _starttime -local _currenttime - -local _maxsendlen -local _maxreadlen - -local _checkinterval -local _sendtimeout -local _readtimeout - -local _cleanqueue - -local _timer - -local _maxclientsperserver - -----------------------------------// DEFINITION //-- - -_server = { } -- key = port, value = table; list of listening servers -_readlist = { } -- array with sockets to read from -_sendlist = { } -- arrary with sockets to write to -_timerlist = { } -- array of timer functions -_socketlist = { } -- key = socket, value = wrapped socket (handlers) -_readtimes = { } -- key = handler, value = timestamp of last data reading -_writetimes = { } -- key = handler, value = timestamp of last data writing/sending -_closelist = { } -- handlers to close - -_readlistlen = 0 -- length of readlist -_sendlistlen = 0 -- length of sendlist -_timerlistlen = 0 -- lenght of timerlist - -_sendtraffic = 0 -- some stats -_readtraffic = 0 - -_selecttimeout = 1 -- timeout of socket.select -_sleeptime = 0 -- time to wait at the end of every loop - -_maxsendlen = 51000 * 1024 -- max len of send buffer -_maxreadlen = 25000 * 1024 -- max len of read buffer - -_checkinterval = 1200000 -- interval in secs to check idle clients -_sendtimeout = 60000 -- allowed send idle time in secs -_readtimeout = 6 * 60 * 60 -- allowed read idle time in secs - -_cleanqueue = false -- clean bufferqueue after using - -_maxclientsperserver = 1000 - -_maxsslhandshake = 30 -- max handshake round-trips -----------------------------------// PRIVATE //-- - -wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxconnections, startssl ) -- this function wraps a server - - maxconnections = maxconnections or _maxclientsperserver - - local connections = 0 - - local dispatch, disconnect = listeners.incoming or listeners.listener, listeners.disconnect - - local err - - local ssl = false - - if sslctx then - ssl = true - if not ssl_newcontext then - out_error "luasec not found" - ssl = false - end - if type( sslctx ) ~= "table" then - out_error "server.lua: wrong server sslctx" - ssl = false - end - local ctx; - ctx, err = ssl_newcontext( sslctx ) - if not ctx then - err = err or "wrong sslctx parameters" - local file; - file = err:match("^error loading (.-) %("); - if file then - if file == "private key" then - file = sslctx.key or "your private key"; - elseif file == "certificate" then - file = sslctx.certificate or "your certificate file"; - end - local reason = err:match("%((.+)%)$") or "some reason"; - if reason == "Permission denied" then - reason = "Check that the permissions allow Prosody to read this file."; - elseif reason == "No such file or directory" then - reason = "Check that the path is correct, and the file exists."; - elseif reason == "system lib" then - reason = "Previous error (see logs), or other system error."; - else - reason = "Reason: "..tostring(reason or "unknown"):lower(); - end - log("error", "SSL/TLS: Failed to load %s: %s", file, reason); - else - log("error", "SSL/TLS: Error initialising for port %d: %s", serverport, err ); - end - ssl = false - end - sslctx = ctx; - end - if not ssl then - sslctx = false; - if startssl then - log("error", "Failed to listen on port %d due to SSL/TLS to SSL/TLS initialisation errors (see logs)", serverport ) - return nil, "Cannot start ssl, see log for details" - end - end - - local accept = socket.accept - - --// public methods of the object //-- - - local handler = { } - - handler.shutdown = function( ) end - - handler.ssl = function( ) - return ssl - end - handler.sslctx = function( ) - return sslctx - end - handler.remove = function( ) - connections = connections - 1 - end - handler.close = function( ) - for _, handler in pairs( _socketlist ) do - if handler.serverport == serverport then - handler.disconnect( handler, "server closed" ) - handler.close( true ) - end - end - socket:close( ) - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) - _readlistlen = removesocket( _readlist, socket, _readlistlen ) - _socketlist[ socket ] = nil - handler = nil - socket = nil - --mem_free( ) - out_put "server.lua: closed server handler and removed sockets from list" - end - handler.ip = function( ) - return ip - end - handler.serverport = function( ) - return serverport - end - handler.socket = function( ) - return socket - end - handler.readbuffer = function( ) - if connections > maxconnections then - out_put( "server.lua: refused new client connection: server full" ) - return false - end - local client, err = accept( socket ) -- try to accept - if client then - local ip, clientport = client:getpeername( ) - client:settimeout( 0 ) - local handler, client, err = wrapconnection( handler, listeners, client, ip, serverport, clientport, pattern, sslctx, startssl ) -- wrap new client socket - if err then -- error while wrapping ssl socket - return false - end - connections = connections + 1 - out_put( "server.lua: accepted new client connection from ", tostring(ip), ":", tostring(clientport), " to ", tostring(serverport)) - return dispatch( handler ) - elseif err then -- maybe timeout or something else - out_put( "server.lua: error with new client connection: ", tostring(err) ) - return false - end - end - return handler -end - -wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx, startssl ) -- this function wraps a client to a handler object - - socket:settimeout( 0 ) - - --// local import of socket methods //-- - - local send - local receive - local shutdown - - --// private closures of the object //-- - - local ssl - - local dispatch = listeners.incoming or listeners.listener - local status = listeners.status - local disconnect = listeners.disconnect - - local bufferqueue = { } -- buffer array - local bufferqueuelen = 0 -- end of buffer array - - local toclose - local fatalerror - local needtls - - local bufferlen = 0 - - local noread = false - local nosend = false - - local sendtraffic, readtraffic = 0, 0 - - local maxsendlen = _maxsendlen - local maxreadlen = _maxreadlen - - --// public methods of the object //-- - - local handler = bufferqueue -- saves a table ^_^ - - handler.dispatch = function( ) - return dispatch - end - handler.disconnect = function( ) - return disconnect - end - handler.setlistener = function( listeners ) - dispatch = listeners.incoming - disconnect = listeners.disconnect - end - handler.getstats = function( ) - return readtraffic, sendtraffic - end - handler.ssl = function( ) - return ssl - end - handler.sslctx = function ( ) - return sslctx - end - handler.send = function( _, data, i, j ) - return send( socket, data, i, j ) - end - handler.receive = function( pattern, prefix ) - return receive( socket, pattern, prefix ) - end - handler.shutdown = function( pattern ) - return shutdown( socket, pattern ) - end - handler.close = function( forced ) - if not handler then return true; end - _readlistlen = removesocket( _readlist, socket, _readlistlen ) - _readtimes[ handler ] = nil - if bufferqueuelen ~= 0 then - if not ( forced or fatalerror ) then - handler.sendbuffer( ) - if bufferqueuelen ~= 0 then -- try again... - if handler then - handler.write = nil -- ... but no further writing allowed - end - toclose = true - return false - end - else - send( socket, table_concat( bufferqueue, "", 1, bufferqueuelen ), 1, bufferlen ) -- forced send - end - end - if socket then - _ = shutdown and shutdown( socket ) - socket:close( ) - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) - _socketlist[ socket ] = nil - socket = nil - else - out_put "server.lua: socket already closed" - end - if handler then - _writetimes[ handler ] = nil - _closelist[ handler ] = nil - handler = nil - end - if server then - server.remove( ) - end - out_put "server.lua: closed client handler and removed socket from list" - return true - end - handler.ip = function( ) - return ip - end - handler.serverport = function( ) - return serverport - end - handler.clientport = function( ) - return clientport - end - local write = function( data ) - bufferlen = bufferlen + string_len( data ) - if bufferlen > maxsendlen then - _closelist[ handler ] = "send buffer exceeded" -- cannot close the client at the moment, have to wait to the end of the cycle - handler.write = idfalse -- dont write anymore - return false - elseif socket and not _sendlist[ socket ] then - _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) - end - bufferqueuelen = bufferqueuelen + 1 - bufferqueue[ bufferqueuelen ] = data - if handler then - _writetimes[ handler ] = _writetimes[ handler ] or _currenttime - end - return true - end - handler.write = write - handler.bufferqueue = function( ) - return bufferqueue - end - handler.socket = function( ) - return socket - end - handler.pattern = function( new ) - pattern = new or pattern - return pattern - end - handler.setsend = function ( newsend ) - send = newsend or send - return send - end - handler.bufferlen = function( readlen, sendlen ) - maxsendlen = sendlen or maxsendlen - maxreadlen = readlen or maxreadlen - return maxreadlen, maxsendlen - end - handler.lock = function( switch ) - if switch == true then - handler.write = idfalse - local tmp = _sendlistlen - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) - _writetimes[ handler ] = nil - if _sendlistlen ~= tmp then - nosend = true - end - tmp = _readlistlen - _readlistlen = removesocket( _readlist, socket, _readlistlen ) - _readtimes[ handler ] = nil - if _readlistlen ~= tmp then - noread = true - end - elseif switch == false then - handler.write = write - if noread then - noread = false - _readlistlen = addsocket(_readlist, socket, _readlistlen) - _readtimes[ handler ] = _currenttime - end - if nosend then - nosend = false - write( "" ) - end - end - return noread, nosend - end - local _readbuffer = function( ) -- this function reads data - local buffer, err, part = receive( socket, pattern ) -- receive buffer with "pattern" - if not err or ( err == "timeout" or err == "wantread" ) then -- received something - local buffer = buffer or part or "" - local len = string_len( buffer ) - if len > maxreadlen then - disconnect( handler, "receive buffer exceeded" ) - handler.close( true ) - return false - end - local count = len * STAT_UNIT - readtraffic = readtraffic + count - _readtraffic = _readtraffic + count - _readtimes[ handler ] = _currenttime - --out_put( "server.lua: read data '", buffer:gsub("[^%w%p ]", "."), "', error: ", err ) - return dispatch( handler, buffer, err ) - else -- connections was closed or fatal error - out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " read error: ", tostring(err) ) - fatalerror = true - disconnect( handler, err ) - _ = handler and handler.close( ) - return false - end - end - local _sendbuffer = function( ) -- this function sends data - local succ, err, byte, buffer, count; - local count; - if socket then - buffer = table_concat( bufferqueue, "", 1, bufferqueuelen ) - succ, err, byte = send( socket, buffer, 1, bufferlen ) - count = ( succ or byte or 0 ) * STAT_UNIT - sendtraffic = sendtraffic + count - _sendtraffic = _sendtraffic + count - _ = _cleanqueue and clean( bufferqueue ) - --out_put( "server.lua: sended '", buffer, "', bytes: ", tostring(succ), ", error: ", tostring(err), ", part: ", tostring(byte), ", to: ", tostring(ip), ":", tostring(clientport) ) - else - succ, err, count = false, "closed", 0; - end - if succ then -- sending succesful - bufferqueuelen = 0 - bufferlen = 0 - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) -- delete socket from writelist - _ = needtls and handler.starttls(true) - _writetimes[ handler ] = nil - _ = toclose and handler.close( ) - return true - elseif byte and ( err == "timeout" or err == "wantwrite" ) then -- want write - buffer = string_sub( buffer, byte + 1, bufferlen ) -- new buffer - bufferqueue[ 1 ] = buffer -- insert new buffer in queue - bufferqueuelen = 1 - bufferlen = bufferlen - byte - _writetimes[ handler ] = _currenttime - return true - else -- connection was closed during sending or fatal error - out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " write error: ", tostring(err) ) - fatalerror = true - disconnect( handler, err ) - _ = handler and handler.close( ) - return false - end - end - - -- Set the sslctx - local handshake; - function handler.set_sslctx(new_sslctx) - ssl = true - sslctx = new_sslctx; - local wrote - local read - handshake = coroutine_wrap( function( client ) -- create handshake coroutine - local err - for i = 1, _maxsslhandshake do - _sendlistlen = ( wrote and removesocket( _sendlist, client, _sendlistlen ) ) or _sendlistlen - _readlistlen = ( read and removesocket( _readlist, client, _readlistlen ) ) or _readlistlen - read, wrote = nil, nil - _, err = client:dohandshake( ) - if not err then - out_put( "server.lua: ssl handshake done" ) - handler.readbuffer = _readbuffer -- when handshake is done, replace the handshake function with regular functions - handler.sendbuffer = _sendbuffer - _ = status and status( handler, "ssl-handshake-complete" ) - _readlistlen = addsocket(_readlist, client, _readlistlen) - return true - else - out_put( "server.lua: error during ssl handshake: ", tostring(err) ) - if err == "wantwrite" and not wrote then - _sendlistlen = addsocket(_sendlist, client, _sendlistlen) - wrote = true - elseif err == "wantread" and not read then - _readlistlen = addsocket(_readlist, client, _readlistlen) - read = true - else - break; - end - --coroutine_yield( handler, nil, err ) -- handshake not finished - coroutine_yield( ) - end - end - disconnect( handler, "ssl handshake failed" ) - _ = handler and handler.close( true ) -- forced disconnect - return false -- handshake failed - end - ) - end - if sslctx then -- ssl? - handler.set_sslctx(sslctx); - if startssl then -- ssl now? - --out_put("server.lua: ", "starting ssl handshake") - local err - socket, err = ssl_wrap( socket, sslctx ) -- wrap socket - if err then - out_put( "server.lua: ssl error: ", tostring(err) ) - --mem_free( ) - return nil, nil, err -- fatal error - end - socket:settimeout( 0 ) - handler.readbuffer = handshake - handler.sendbuffer = handshake - handshake( socket ) -- do handshake - if not socket then - return nil, nil, "ssl handshake failed"; - end - else - -- We're not automatically doing SSL, so we're not secure (yet) - ssl = false - handler.starttls = function( now ) - if not now then - --out_put "server.lua: we need to do tls, but delaying until later" - needtls = true - return - end - --out_put( "server.lua: attempting to start tls on " .. tostring( socket ) ) - local oldsocket, err = socket - socket, err = ssl_wrap( socket, sslctx ) -- wrap socket - --out_put( "server.lua: sslwrapped socket is " .. tostring( socket ) ) - if err then - out_put( "server.lua: error while starting tls on client: ", tostring(err) ) - return nil, err -- fatal error - end - - socket:settimeout( 0 ) - - -- add the new socket to our system - - send = socket.send - receive = socket.receive - shutdown = id - - _socketlist[ socket ] = handler - _readlistlen = addsocket(_readlist, socket, _readlistlen) - - -- remove traces of the old socket - - _readlistlen = removesocket( _readlist, oldsocket, _readlistlen ) - _sendlistlen = removesocket( _sendlist, oldsocket, _sendlistlen ) - _socketlist[ oldsocket ] = nil - - handler.starttls = nil - needtls = nil - - -- Secure now - ssl = true - - handler.readbuffer = handshake - handler.sendbuffer = handshake - handshake( socket ) -- do handshake - end - handler.readbuffer = _readbuffer - handler.sendbuffer = _sendbuffer - end - else -- normal connection - ssl = false - handler.readbuffer = _readbuffer - handler.sendbuffer = _sendbuffer - end - - send = socket.send - receive = socket.receive - shutdown = ( ssl and id ) or socket.shutdown - - _socketlist[ socket ] = handler - _readlistlen = addsocket(_readlist, socket, _readlistlen) - - return handler, socket -end - -id = function( ) -end - -idfalse = function( ) - return false -end - -addsocket = function( list, socket, len ) - if not list[ socket ] then - len = len + 1 - list[ len ] = socket - list[ socket ] = len - end - return len; -end - -removesocket = function( list, socket, len ) -- this function removes sockets from a list ( copied from copas ) - local pos = list[ socket ] - if pos then - list[ socket ] = nil - local last = list[ len ] - list[ len ] = nil - if last ~= socket then - list[ last ] = pos - list[ pos ] = last - end - return len - 1 - end - return len -end - -closesocket = function( socket ) - _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) - _readlistlen = removesocket( _readlist, socket, _readlistlen ) - _socketlist[ socket ] = nil - socket:close( ) - --mem_free( ) -end - -----------------------------------// PUBLIC //-- - -addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, startssl ) -- this function provides a way for other scripts to reg a server - local err - --out_put("server.lua: autossl on ", port, " is ", startssl) - if type( listeners ) ~= "table" then - err = "invalid listener table" - end - if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then - err = "invalid port" - elseif _server[ port ] then - err = "listeners on port '" .. port .. "' already exist" - elseif sslctx and not luasec then - err = "luasec not found" - end - if err then - out_error( "server.lua, port ", port, ": ", err ) - return nil, err - end - addr = addr or "*" - local server, err = socket_bind( addr, port ) - if err then - out_error( "server.lua, port ", port, ": ", err ) - return nil, err - end - local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, maxconnections, startssl ) -- wrap new server socket - if not handler then - server:close( ) - return nil, err - end - server:settimeout( 0 ) - _readlistlen = addsocket(_readlist, server, _readlistlen) - _server[ port ] = handler - _socketlist[ server ] = handler - out_put( "server.lua: new server listener on '", addr, ":", port, "'" ) - return handler -end - -getserver = function ( port ) - return _server[ port ]; -end - -removeserver = function( port ) - local handler = _server[ port ] - if not handler then - return nil, "no server found on port '" .. tostring( port ) .. "'" - end - handler.close( ) - _server[ port ] = nil - return true -end - -closeall = function( ) - for _, handler in pairs( _socketlist ) do - handler.close( ) - _socketlist[ _ ] = nil - end - _readlistlen = 0 - _sendlistlen = 0 - _timerlistlen = 0 - _server = { } - _readlist = { } - _sendlist = { } - _timerlist = { } - _socketlist = { } - --mem_free( ) -end - -getsettings = function( ) - return _selecttimeout, _sleeptime, _maxsendlen, _maxreadlen, _checkinterval, _sendtimeout, _readtimeout, _cleanqueue, _maxclientsperserver, _maxsslhandshake -end - -changesettings = function( new ) - if type( new ) ~= "table" then - return nil, "invalid settings table" - end - _selecttimeout = tonumber( new.timeout ) or _selecttimeout - _sleeptime = tonumber( new.sleeptime ) or _sleeptime - _maxsendlen = tonumber( new.maxsendlen ) or _maxsendlen - _maxreadlen = tonumber( new.maxreadlen ) or _maxreadlen - _checkinterval = tonumber( new.checkinterval ) or _checkinterval - _sendtimeout = tonumber( new.sendtimeout ) or _sendtimeout - _readtimeout = tonumber( new.readtimeout ) or _readtimeout - _cleanqueue = new.cleanqueue - _maxclientsperserver = new._maxclientsperserver or _maxclientsperserver - _maxsslhandshake = new._maxsslhandshake or _maxsslhandshake - return true -end - -addtimer = function( listener ) - if type( listener ) ~= "function" then - return nil, "invalid listener function" - end - _timerlistlen = _timerlistlen + 1 - _timerlist[ _timerlistlen ] = listener - return true -end - -stats = function( ) - return _readtraffic, _sendtraffic, _readlistlen, _sendlistlen, _timerlistlen -end - -local dontstop = true; -- thinking about tomorrow, ... - -setquitting = function (quit) - dontstop = not quit; - return; -end - -loop = function( ) -- this is the main loop of the program - while dontstop do - local read, write, err = socket_select( _readlist, _sendlist, _selecttimeout ) - for i, socket in ipairs( write ) do -- send data waiting in writequeues - local handler = _socketlist[ socket ] - if handler then - handler.sendbuffer( ) - else - closesocket( socket ) - out_put "server.lua: found no handler and closed socket (writelist)" -- this should not happen - end - end - for i, socket in ipairs( read ) do -- receive data - local handler = _socketlist[ socket ] - if handler then - handler.readbuffer( ) - else - closesocket( socket ) - out_put "server.lua: found no handler and closed socket (readlist)" -- this can happen - end - end - for handler, err in pairs( _closelist ) do - handler.disconnect( )( handler, err ) - handler.close( true ) -- forced disconnect - end - clean( _closelist ) - _currenttime = os_time( ) - if os_difftime( _currenttime - _timer ) >= 1 then - for i = 1, _timerlistlen do - _timerlist[ i ]( _currenttime ) -- fire timers - end - _timer = _currenttime - end - socket_sleep( _sleeptime ) -- wait some time - --collectgarbage( ) - end - return "quitting" -end - ---// EXPERIMENTAL //-- - -local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx, startssl ) - local handler = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx, startssl ) - _socketlist[ socket ] = handler - _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) - return handler, socket -end - -local addclient = function( address, port, listeners, pattern, sslctx, startssl ) - local client, err = luasocket.tcp( ) - if err then - return nil, err - end - client:settimeout( 0 ) - _, err = client:connect( address, port ) - if err then -- try again - local handler = wrapclient( client, address, port, listeners ) - else - wrapconnection( nil, listeners, client, address, port, "clientport", pattern, sslctx, startssl ) - end -end - ---// EXPERIMENTAL //-- - -----------------------------------// BEGIN //-- - -use "setmetatable" ( _socketlist, { __mode = "k" } ) -use "setmetatable" ( _readtimes, { __mode = "k" } ) -use "setmetatable" ( _writetimes, { __mode = "k" } ) - -_timer = os_time( ) -_starttime = os_time( ) - -addtimer( function( ) - local difftime = os_difftime( _currenttime - _starttime ) - if difftime > _checkinterval then - _starttime = _currenttime - for handler, timestamp in pairs( _writetimes ) do - if os_difftime( _currenttime - timestamp ) > _sendtimeout then - --_writetimes[ handler ] = nil - handler.disconnect( )( handler, "send timeout" ) - handler.close( true ) -- forced disconnect - end - end - for handler, timestamp in pairs( _readtimes ) do - if os_difftime( _currenttime - timestamp ) > _readtimeout then - --_readtimes[ handler ] = nil - handler.disconnect( )( handler, "read timeout" ) - handler.close( ) -- forced disconnect? - end - end - end - end -) - -----------------------------------// PUBLIC INTERFACE //-- - -return { - - addclient = addclient, - wrapclient = wrapclient, - - loop = loop, - stats = stats, - closeall = closeall, - addtimer = addtimer, - addserver = addserver, - getserver = getserver, - getsettings = getsettings, - setquitting = setquitting, - removeserver = removeserver, - changesettings = changesettings, -} diff --git a/net/server_select.lua b/net/server_select.lua new file mode 100644 index 00000000..6ab8ce91 --- /dev/null +++ b/net/server_select.lua @@ -0,0 +1,914 @@ +-- +-- server.lua by blastbeat of the luadch project +-- Re-used here under the MIT/X Consortium License +-- +-- Modifications (C) 2008-2009 Matthew Wild, Waqas Hussain +-- + +-- // wrapping luadch stuff // -- + +local use = function( what ) + return _G[ what ] +end +local clean = function( tbl ) + for i, k in pairs( tbl ) do + tbl[ i ] = nil + end +end + +local log, table_concat = require ("util.logger").init("socket"), table.concat; +local out_put = function (...) return log("debug", table_concat{...}); end +local out_error = function (...) return log("warn", table_concat{...}); end +local mem_free = collectgarbage + +----------------------------------// DECLARATION //-- + +--// constants //-- + +local STAT_UNIT = 1 -- byte + +--// lua functions //-- + +local type = use "type" +local pairs = use "pairs" +local ipairs = use "ipairs" +local tostring = use "tostring" +local collectgarbage = use "collectgarbage" + +--// lua libs //-- + +local os = use "os" +local table = use "table" +local string = use "string" +local coroutine = use "coroutine" + +--// lua lib methods //-- + +local os_time = os.time +local os_difftime = os.difftime +local table_concat = table.concat +local table_remove = table.remove +local string_len = string.len +local string_sub = string.sub +local coroutine_wrap = coroutine.wrap +local coroutine_yield = coroutine.yield + +--// extern libs //-- + +local luasec = select( 2, pcall( require, "ssl" ) ) +local luasocket = require "socket" + +--// extern lib methods //-- + +local ssl_wrap = ( luasec and luasec.wrap ) +local socket_bind = luasocket.bind +local socket_sleep = luasocket.sleep +local socket_select = luasocket.select +local ssl_newcontext = ( luasec and luasec.newcontext ) + +--// functions //-- + +local id +local loop +local stats +local idfalse +local addtimer +local closeall +local addserver +local getserver +local wrapserver +local getsettings +local closesocket +local removesocket +local removeserver +local changetimeout +local wrapconnection +local changesettings + +--// tables //-- + +local _server +local _readlist +local _timerlist +local _sendlist +local _socketlist +local _closelist +local _readtimes +local _writetimes + +--// simple data types //-- + +local _ +local _readlistlen +local _sendlistlen +local _timerlistlen + +local _sendtraffic +local _readtraffic + +local _selecttimeout +local _sleeptime + +local _starttime +local _currenttime + +local _maxsendlen +local _maxreadlen + +local _checkinterval +local _sendtimeout +local _readtimeout + +local _cleanqueue + +local _timer + +local _maxclientsperserver + +----------------------------------// DEFINITION //-- + +_server = { } -- key = port, value = table; list of listening servers +_readlist = { } -- array with sockets to read from +_sendlist = { } -- arrary with sockets to write to +_timerlist = { } -- array of timer functions +_socketlist = { } -- key = socket, value = wrapped socket (handlers) +_readtimes = { } -- key = handler, value = timestamp of last data reading +_writetimes = { } -- key = handler, value = timestamp of last data writing/sending +_closelist = { } -- handlers to close + +_readlistlen = 0 -- length of readlist +_sendlistlen = 0 -- length of sendlist +_timerlistlen = 0 -- lenght of timerlist + +_sendtraffic = 0 -- some stats +_readtraffic = 0 + +_selecttimeout = 1 -- timeout of socket.select +_sleeptime = 0 -- time to wait at the end of every loop + +_maxsendlen = 51000 * 1024 -- max len of send buffer +_maxreadlen = 25000 * 1024 -- max len of read buffer + +_checkinterval = 1200000 -- interval in secs to check idle clients +_sendtimeout = 60000 -- allowed send idle time in secs +_readtimeout = 6 * 60 * 60 -- allowed read idle time in secs + +_cleanqueue = false -- clean bufferqueue after using + +_maxclientsperserver = 1000 + +_maxsslhandshake = 30 -- max handshake round-trips +----------------------------------// PRIVATE //-- + +wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxconnections, startssl ) -- this function wraps a server + + maxconnections = maxconnections or _maxclientsperserver + + local connections = 0 + + local dispatch, disconnect = listeners.incoming or listeners.listener, listeners.disconnect + + local err + + local ssl = false + + if sslctx then + ssl = true + if not ssl_newcontext then + out_error "luasec not found" + ssl = false + end + if type( sslctx ) ~= "table" then + out_error "server.lua: wrong server sslctx" + ssl = false + end + local ctx; + ctx, err = ssl_newcontext( sslctx ) + if not ctx then + err = err or "wrong sslctx parameters" + local file; + file = err:match("^error loading (.-) %("); + if file then + if file == "private key" then + file = sslctx.key or "your private key"; + elseif file == "certificate" then + file = sslctx.certificate or "your certificate file"; + end + local reason = err:match("%((.+)%)$") or "some reason"; + if reason == "Permission denied" then + reason = "Check that the permissions allow Prosody to read this file."; + elseif reason == "No such file or directory" then + reason = "Check that the path is correct, and the file exists."; + elseif reason == "system lib" then + reason = "Previous error (see logs), or other system error."; + else + reason = "Reason: "..tostring(reason or "unknown"):lower(); + end + log("error", "SSL/TLS: Failed to load %s: %s", file, reason); + else + log("error", "SSL/TLS: Error initialising for port %d: %s", serverport, err ); + end + ssl = false + end + sslctx = ctx; + end + if not ssl then + sslctx = false; + if startssl then + log("error", "Failed to listen on port %d due to SSL/TLS to SSL/TLS initialisation errors (see logs)", serverport ) + return nil, "Cannot start ssl, see log for details" + end + end + + local accept = socket.accept + + --// public methods of the object //-- + + local handler = { } + + handler.shutdown = function( ) end + + handler.ssl = function( ) + return ssl + end + handler.sslctx = function( ) + return sslctx + end + handler.remove = function( ) + connections = connections - 1 + end + handler.close = function( ) + for _, handler in pairs( _socketlist ) do + if handler.serverport == serverport then + handler.disconnect( handler, "server closed" ) + handler.close( true ) + end + end + socket:close( ) + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) + _readlistlen = removesocket( _readlist, socket, _readlistlen ) + _socketlist[ socket ] = nil + handler = nil + socket = nil + --mem_free( ) + out_put "server.lua: closed server handler and removed sockets from list" + end + handler.ip = function( ) + return ip + end + handler.serverport = function( ) + return serverport + end + handler.socket = function( ) + return socket + end + handler.readbuffer = function( ) + if connections > maxconnections then + out_put( "server.lua: refused new client connection: server full" ) + return false + end + local client, err = accept( socket ) -- try to accept + if client then + local ip, clientport = client:getpeername( ) + client:settimeout( 0 ) + local handler, client, err = wrapconnection( handler, listeners, client, ip, serverport, clientport, pattern, sslctx, startssl ) -- wrap new client socket + if err then -- error while wrapping ssl socket + return false + end + connections = connections + 1 + out_put( "server.lua: accepted new client connection from ", tostring(ip), ":", tostring(clientport), " to ", tostring(serverport)) + return dispatch( handler ) + elseif err then -- maybe timeout or something else + out_put( "server.lua: error with new client connection: ", tostring(err) ) + return false + end + end + return handler +end + +wrapconnection = function( server, listeners, socket, ip, serverport, clientport, pattern, sslctx, startssl ) -- this function wraps a client to a handler object + + socket:settimeout( 0 ) + + --// local import of socket methods //-- + + local send + local receive + local shutdown + + --// private closures of the object //-- + + local ssl + + local dispatch = listeners.incoming or listeners.listener + local status = listeners.status + local disconnect = listeners.disconnect + + local bufferqueue = { } -- buffer array + local bufferqueuelen = 0 -- end of buffer array + + local toclose + local fatalerror + local needtls + + local bufferlen = 0 + + local noread = false + local nosend = false + + local sendtraffic, readtraffic = 0, 0 + + local maxsendlen = _maxsendlen + local maxreadlen = _maxreadlen + + --// public methods of the object //-- + + local handler = bufferqueue -- saves a table ^_^ + + handler.dispatch = function( ) + return dispatch + end + handler.disconnect = function( ) + return disconnect + end + handler.setlistener = function( listeners ) + dispatch = listeners.incoming + disconnect = listeners.disconnect + end + handler.getstats = function( ) + return readtraffic, sendtraffic + end + handler.ssl = function( ) + return ssl + end + handler.sslctx = function ( ) + return sslctx + end + handler.send = function( _, data, i, j ) + return send( socket, data, i, j ) + end + handler.receive = function( pattern, prefix ) + return receive( socket, pattern, prefix ) + end + handler.shutdown = function( pattern ) + return shutdown( socket, pattern ) + end + handler.close = function( forced ) + if not handler then return true; end + _readlistlen = removesocket( _readlist, socket, _readlistlen ) + _readtimes[ handler ] = nil + if bufferqueuelen ~= 0 then + if not ( forced or fatalerror ) then + handler.sendbuffer( ) + if bufferqueuelen ~= 0 then -- try again... + if handler then + handler.write = nil -- ... but no further writing allowed + end + toclose = true + return false + end + else + send( socket, table_concat( bufferqueue, "", 1, bufferqueuelen ), 1, bufferlen ) -- forced send + end + end + if socket then + _ = shutdown and shutdown( socket ) + socket:close( ) + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) + _socketlist[ socket ] = nil + socket = nil + else + out_put "server.lua: socket already closed" + end + if handler then + _writetimes[ handler ] = nil + _closelist[ handler ] = nil + handler = nil + end + if server then + server.remove( ) + end + out_put "server.lua: closed client handler and removed socket from list" + return true + end + handler.ip = function( ) + return ip + end + handler.serverport = function( ) + return serverport + end + handler.clientport = function( ) + return clientport + end + local write = function( data ) + bufferlen = bufferlen + string_len( data ) + if bufferlen > maxsendlen then + _closelist[ handler ] = "send buffer exceeded" -- cannot close the client at the moment, have to wait to the end of the cycle + handler.write = idfalse -- dont write anymore + return false + elseif socket and not _sendlist[ socket ] then + _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) + end + bufferqueuelen = bufferqueuelen + 1 + bufferqueue[ bufferqueuelen ] = data + if handler then + _writetimes[ handler ] = _writetimes[ handler ] or _currenttime + end + return true + end + handler.write = write + handler.bufferqueue = function( ) + return bufferqueue + end + handler.socket = function( ) + return socket + end + handler.pattern = function( new ) + pattern = new or pattern + return pattern + end + handler.setsend = function ( newsend ) + send = newsend or send + return send + end + handler.bufferlen = function( readlen, sendlen ) + maxsendlen = sendlen or maxsendlen + maxreadlen = readlen or maxreadlen + return maxreadlen, maxsendlen + end + handler.lock = function( switch ) + if switch == true then + handler.write = idfalse + local tmp = _sendlistlen + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) + _writetimes[ handler ] = nil + if _sendlistlen ~= tmp then + nosend = true + end + tmp = _readlistlen + _readlistlen = removesocket( _readlist, socket, _readlistlen ) + _readtimes[ handler ] = nil + if _readlistlen ~= tmp then + noread = true + end + elseif switch == false then + handler.write = write + if noread then + noread = false + _readlistlen = addsocket(_readlist, socket, _readlistlen) + _readtimes[ handler ] = _currenttime + end + if nosend then + nosend = false + write( "" ) + end + end + return noread, nosend + end + local _readbuffer = function( ) -- this function reads data + local buffer, err, part = receive( socket, pattern ) -- receive buffer with "pattern" + if not err or ( err == "timeout" or err == "wantread" ) then -- received something + local buffer = buffer or part or "" + local len = string_len( buffer ) + if len > maxreadlen then + disconnect( handler, "receive buffer exceeded" ) + handler.close( true ) + return false + end + local count = len * STAT_UNIT + readtraffic = readtraffic + count + _readtraffic = _readtraffic + count + _readtimes[ handler ] = _currenttime + --out_put( "server.lua: read data '", buffer:gsub("[^%w%p ]", "."), "', error: ", err ) + return dispatch( handler, buffer, err ) + else -- connections was closed or fatal error + out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " read error: ", tostring(err) ) + fatalerror = true + disconnect( handler, err ) + _ = handler and handler.close( ) + return false + end + end + local _sendbuffer = function( ) -- this function sends data + local succ, err, byte, buffer, count; + local count; + if socket then + buffer = table_concat( bufferqueue, "", 1, bufferqueuelen ) + succ, err, byte = send( socket, buffer, 1, bufferlen ) + count = ( succ or byte or 0 ) * STAT_UNIT + sendtraffic = sendtraffic + count + _sendtraffic = _sendtraffic + count + _ = _cleanqueue and clean( bufferqueue ) + --out_put( "server.lua: sended '", buffer, "', bytes: ", tostring(succ), ", error: ", tostring(err), ", part: ", tostring(byte), ", to: ", tostring(ip), ":", tostring(clientport) ) + else + succ, err, count = false, "closed", 0; + end + if succ then -- sending succesful + bufferqueuelen = 0 + bufferlen = 0 + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) -- delete socket from writelist + _ = needtls and handler.starttls(true) + _writetimes[ handler ] = nil + _ = toclose and handler.close( ) + return true + elseif byte and ( err == "timeout" or err == "wantwrite" ) then -- want write + buffer = string_sub( buffer, byte + 1, bufferlen ) -- new buffer + bufferqueue[ 1 ] = buffer -- insert new buffer in queue + bufferqueuelen = 1 + bufferlen = bufferlen - byte + _writetimes[ handler ] = _currenttime + return true + else -- connection was closed during sending or fatal error + out_put( "server.lua: client ", tostring(ip), ":", tostring(clientport), " write error: ", tostring(err) ) + fatalerror = true + disconnect( handler, err ) + _ = handler and handler.close( ) + return false + end + end + + -- Set the sslctx + local handshake; + function handler.set_sslctx(new_sslctx) + ssl = true + sslctx = new_sslctx; + local wrote + local read + handshake = coroutine_wrap( function( client ) -- create handshake coroutine + local err + for i = 1, _maxsslhandshake do + _sendlistlen = ( wrote and removesocket( _sendlist, client, _sendlistlen ) ) or _sendlistlen + _readlistlen = ( read and removesocket( _readlist, client, _readlistlen ) ) or _readlistlen + read, wrote = nil, nil + _, err = client:dohandshake( ) + if not err then + out_put( "server.lua: ssl handshake done" ) + handler.readbuffer = _readbuffer -- when handshake is done, replace the handshake function with regular functions + handler.sendbuffer = _sendbuffer + _ = status and status( handler, "ssl-handshake-complete" ) + _readlistlen = addsocket(_readlist, client, _readlistlen) + return true + else + out_put( "server.lua: error during ssl handshake: ", tostring(err) ) + if err == "wantwrite" and not wrote then + _sendlistlen = addsocket(_sendlist, client, _sendlistlen) + wrote = true + elseif err == "wantread" and not read then + _readlistlen = addsocket(_readlist, client, _readlistlen) + read = true + else + break; + end + --coroutine_yield( handler, nil, err ) -- handshake not finished + coroutine_yield( ) + end + end + disconnect( handler, "ssl handshake failed" ) + _ = handler and handler.close( true ) -- forced disconnect + return false -- handshake failed + end + ) + end + if sslctx then -- ssl? + handler.set_sslctx(sslctx); + if startssl then -- ssl now? + --out_put("server.lua: ", "starting ssl handshake") + local err + socket, err = ssl_wrap( socket, sslctx ) -- wrap socket + if err then + out_put( "server.lua: ssl error: ", tostring(err) ) + --mem_free( ) + return nil, nil, err -- fatal error + end + socket:settimeout( 0 ) + handler.readbuffer = handshake + handler.sendbuffer = handshake + handshake( socket ) -- do handshake + if not socket then + return nil, nil, "ssl handshake failed"; + end + else + -- We're not automatically doing SSL, so we're not secure (yet) + ssl = false + handler.starttls = function( now ) + if not now then + --out_put "server.lua: we need to do tls, but delaying until later" + needtls = true + return + end + --out_put( "server.lua: attempting to start tls on " .. tostring( socket ) ) + local oldsocket, err = socket + socket, err = ssl_wrap( socket, sslctx ) -- wrap socket + --out_put( "server.lua: sslwrapped socket is " .. tostring( socket ) ) + if err then + out_put( "server.lua: error while starting tls on client: ", tostring(err) ) + return nil, err -- fatal error + end + + socket:settimeout( 0 ) + + -- add the new socket to our system + + send = socket.send + receive = socket.receive + shutdown = id + + _socketlist[ socket ] = handler + _readlistlen = addsocket(_readlist, socket, _readlistlen) + + -- remove traces of the old socket + + _readlistlen = removesocket( _readlist, oldsocket, _readlistlen ) + _sendlistlen = removesocket( _sendlist, oldsocket, _sendlistlen ) + _socketlist[ oldsocket ] = nil + + handler.starttls = nil + needtls = nil + + -- Secure now + ssl = true + + handler.readbuffer = handshake + handler.sendbuffer = handshake + handshake( socket ) -- do handshake + end + handler.readbuffer = _readbuffer + handler.sendbuffer = _sendbuffer + end + else -- normal connection + ssl = false + handler.readbuffer = _readbuffer + handler.sendbuffer = _sendbuffer + end + + send = socket.send + receive = socket.receive + shutdown = ( ssl and id ) or socket.shutdown + + _socketlist[ socket ] = handler + _readlistlen = addsocket(_readlist, socket, _readlistlen) + + return handler, socket +end + +id = function( ) +end + +idfalse = function( ) + return false +end + +addsocket = function( list, socket, len ) + if not list[ socket ] then + len = len + 1 + list[ len ] = socket + list[ socket ] = len + end + return len; +end + +removesocket = function( list, socket, len ) -- this function removes sockets from a list ( copied from copas ) + local pos = list[ socket ] + if pos then + list[ socket ] = nil + local last = list[ len ] + list[ len ] = nil + if last ~= socket then + list[ last ] = pos + list[ pos ] = last + end + return len - 1 + end + return len +end + +closesocket = function( socket ) + _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) + _readlistlen = removesocket( _readlist, socket, _readlistlen ) + _socketlist[ socket ] = nil + socket:close( ) + --mem_free( ) +end + +----------------------------------// PUBLIC //-- + +addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, startssl ) -- this function provides a way for other scripts to reg a server + local err + --out_put("server.lua: autossl on ", port, " is ", startssl) + if type( listeners ) ~= "table" then + err = "invalid listener table" + end + if not type( port ) == "number" or not ( port >= 0 and port <= 65535 ) then + err = "invalid port" + elseif _server[ port ] then + err = "listeners on port '" .. port .. "' already exist" + elseif sslctx and not luasec then + err = "luasec not found" + end + if err then + out_error( "server.lua, port ", port, ": ", err ) + return nil, err + end + addr = addr or "*" + local server, err = socket_bind( addr, port ) + if err then + out_error( "server.lua, port ", port, ": ", err ) + return nil, err + end + local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, maxconnections, startssl ) -- wrap new server socket + if not handler then + server:close( ) + return nil, err + end + server:settimeout( 0 ) + _readlistlen = addsocket(_readlist, server, _readlistlen) + _server[ port ] = handler + _socketlist[ server ] = handler + out_put( "server.lua: new server listener on '", addr, ":", port, "'" ) + return handler +end + +getserver = function ( port ) + return _server[ port ]; +end + +removeserver = function( port ) + local handler = _server[ port ] + if not handler then + return nil, "no server found on port '" .. tostring( port ) .. "'" + end + handler.close( ) + _server[ port ] = nil + return true +end + +closeall = function( ) + for _, handler in pairs( _socketlist ) do + handler.close( ) + _socketlist[ _ ] = nil + end + _readlistlen = 0 + _sendlistlen = 0 + _timerlistlen = 0 + _server = { } + _readlist = { } + _sendlist = { } + _timerlist = { } + _socketlist = { } + --mem_free( ) +end + +getsettings = function( ) + return _selecttimeout, _sleeptime, _maxsendlen, _maxreadlen, _checkinterval, _sendtimeout, _readtimeout, _cleanqueue, _maxclientsperserver, _maxsslhandshake +end + +changesettings = function( new ) + if type( new ) ~= "table" then + return nil, "invalid settings table" + end + _selecttimeout = tonumber( new.timeout ) or _selecttimeout + _sleeptime = tonumber( new.sleeptime ) or _sleeptime + _maxsendlen = tonumber( new.maxsendlen ) or _maxsendlen + _maxreadlen = tonumber( new.maxreadlen ) or _maxreadlen + _checkinterval = tonumber( new.checkinterval ) or _checkinterval + _sendtimeout = tonumber( new.sendtimeout ) or _sendtimeout + _readtimeout = tonumber( new.readtimeout ) or _readtimeout + _cleanqueue = new.cleanqueue + _maxclientsperserver = new._maxclientsperserver or _maxclientsperserver + _maxsslhandshake = new._maxsslhandshake or _maxsslhandshake + return true +end + +addtimer = function( listener ) + if type( listener ) ~= "function" then + return nil, "invalid listener function" + end + _timerlistlen = _timerlistlen + 1 + _timerlist[ _timerlistlen ] = listener + return true +end + +stats = function( ) + return _readtraffic, _sendtraffic, _readlistlen, _sendlistlen, _timerlistlen +end + +local dontstop = true; -- thinking about tomorrow, ... + +setquitting = function (quit) + dontstop = not quit; + return; +end + +loop = function( ) -- this is the main loop of the program + while dontstop do + local read, write, err = socket_select( _readlist, _sendlist, _selecttimeout ) + for i, socket in ipairs( write ) do -- send data waiting in writequeues + local handler = _socketlist[ socket ] + if handler then + handler.sendbuffer( ) + else + closesocket( socket ) + out_put "server.lua: found no handler and closed socket (writelist)" -- this should not happen + end + end + for i, socket in ipairs( read ) do -- receive data + local handler = _socketlist[ socket ] + if handler then + handler.readbuffer( ) + else + closesocket( socket ) + out_put "server.lua: found no handler and closed socket (readlist)" -- this can happen + end + end + for handler, err in pairs( _closelist ) do + handler.disconnect( )( handler, err ) + handler.close( true ) -- forced disconnect + end + clean( _closelist ) + _currenttime = os_time( ) + if os_difftime( _currenttime - _timer ) >= 1 then + for i = 1, _timerlistlen do + _timerlist[ i ]( _currenttime ) -- fire timers + end + _timer = _currenttime + end + socket_sleep( _sleeptime ) -- wait some time + --collectgarbage( ) + end + return "quitting" +end + +--// EXPERIMENTAL //-- + +local wrapclient = function( socket, ip, serverport, listeners, pattern, sslctx, startssl ) + local handler = wrapconnection( nil, listeners, socket, ip, serverport, "clientport", pattern, sslctx, startssl ) + _socketlist[ socket ] = handler + _sendlistlen = addsocket(_sendlist, socket, _sendlistlen) + return handler, socket +end + +local addclient = function( address, port, listeners, pattern, sslctx, startssl ) + local client, err = luasocket.tcp( ) + if err then + return nil, err + end + client:settimeout( 0 ) + _, err = client:connect( address, port ) + if err then -- try again + local handler = wrapclient( client, address, port, listeners ) + else + wrapconnection( nil, listeners, client, address, port, "clientport", pattern, sslctx, startssl ) + end +end + +--// EXPERIMENTAL //-- + +----------------------------------// BEGIN //-- + +use "setmetatable" ( _socketlist, { __mode = "k" } ) +use "setmetatable" ( _readtimes, { __mode = "k" } ) +use "setmetatable" ( _writetimes, { __mode = "k" } ) + +_timer = os_time( ) +_starttime = os_time( ) + +addtimer( function( ) + local difftime = os_difftime( _currenttime - _starttime ) + if difftime > _checkinterval then + _starttime = _currenttime + for handler, timestamp in pairs( _writetimes ) do + if os_difftime( _currenttime - timestamp ) > _sendtimeout then + --_writetimes[ handler ] = nil + handler.disconnect( )( handler, "send timeout" ) + handler.close( true ) -- forced disconnect + end + end + for handler, timestamp in pairs( _readtimes ) do + if os_difftime( _currenttime - timestamp ) > _readtimeout then + --_readtimes[ handler ] = nil + handler.disconnect( )( handler, "read timeout" ) + handler.close( ) -- forced disconnect? + end + end + end + end +) + +----------------------------------// PUBLIC INTERFACE //-- + +return { + + addclient = addclient, + wrapclient = wrapclient, + + loop = loop, + stats = stats, + closeall = closeall, + addtimer = addtimer, + addserver = addserver, + getserver = getserver, + getsettings = getsettings, + setquitting = setquitting, + removeserver = removeserver, + changesettings = changesettings, +} -- cgit v1.2.3 From 2a8668dd30b4e52cd7e7f916527ae31694f01701 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 02:39:08 +0000 Subject: net.server: New net.server to choose the appropriate library from server_select/server_event based on the availability of luaevent and the use_libevent config option --- net/server.lua | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 net/server.lua diff --git a/net/server.lua b/net/server.lua new file mode 100644 index 00000000..507bd81e --- /dev/null +++ b/net/server.lua @@ -0,0 +1,21 @@ +local have_luaevent = pcall(require, "luaevent.core"); +local use_luaevent = require "core.configmanager".get("*", "core", "use_libevent"); + +local server; + +if have_luaevent and use_luaevent == true then + server = require "net.server_event"; + package.loaded["net.server"] = server; + + -- Backwards compatibility for timers, addtimer + -- called a function roughly every second + local add_task = require "util.timer"; + function server.addtimer(f) + return add_task(1, function (...) f(...); return 1; end); + end +else + server = require "net.server_select"; + package.loaded["net.server"] = server; +end + +return server; -- cgit v1.2.3 From 89fa12376ae294bde2e264a665d8e22373bc5a52 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 02:40:21 +0000 Subject: util.timer: Use libevent for lightweight timers if available and configured (use_libevent option) --- util/timer.lua | 68 +++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/util/timer.lua b/util/timer.lua index c0c7f25a..4c9a3ea9 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -8,6 +8,8 @@ local ns_addtimer = require "net.server".addtimer; +local event = require "net.server".event; + local get_time = os.time; local t_insert = table.insert; local t_remove = table.remove; @@ -19,33 +21,51 @@ local new_data = {}; module "timer" -local function _add_task(delay, func) - local current_time = get_time(); - delay = delay + current_time; - if delay >= current_time then - t_insert(new_data, {delay, func}); - else func(); end -end - -add_task = _add_task; - -ns_addtimer(function() - local current_time = get_time(); - if #new_data > 0 then - for _, d in pairs(new_data) do - t_insert(data, d); +local _add_task; +if not event then + function _add_task(delay, func) + local current_time = get_time(); + delay = delay + current_time; + if delay >= current_time then + t_insert(new_data, {delay, func}); + else + func(); end - new_data = {}; end - - for i, d in pairs(data) do - local t, func = d[1], d[2]; - if t <= current_time then - data[i] = nil; - local r = func(current_time); - if type(r) == "number" then _add_task(r, func); end + + ns_addtimer(function() + local current_time = get_time(); + if #new_data > 0 then + for _, d in pairs(new_data) do + t_insert(data, d); + end + new_data = {}; end + + for i, d in pairs(data) do + local t, func = d[1], d[2]; + if t <= current_time then + data[i] = nil; + local r = func(current_time); + if type(r) == "number" then _add_task(r, func); end + end + end + end); +else + local EVENT_LEAVE = (event.core and event.core.LEAVE) or -1; + function _add_task(delay, func) + event.base:addevent(nil, event.EV_TIMEOUT, function () + local ret = func(); + if ret then + _add_task(ret, func); + else + return EVENT_LEAVE; + end + end + , delay); end -end); +end + +add_task = _add_task; return _M; -- cgit v1.2.3 From 0c4e6e38e72dd3d447125dda36db091eb8a8214e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 02:41:09 +0000 Subject: net.server_event: Comment overly verbose log message --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index fab617b7..82046d24 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -640,7 +640,7 @@ end local addserver = ( function( ) return function( addr, port, listener, pattern, backlog, sslcfg, startssl ) -- TODO: check arguments - debug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslcfg or "nil", startssl or "nil") + --vdebug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslcfg or "nil", startssl or "nil") local server, err = socket.bind( addr, port, backlog ) -- create server socket if not server then debug( "creating server socket failed because:", err ) -- cgit v1.2.3 From 27032e69558352d9d91c26f90894411ef64d0d0b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 03:15:59 +0000 Subject: net.server_event: Export base as event_base --- net/server_event.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/server_event.lua b/net/server_event.lua index 82046d24..375ab47e 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -740,6 +740,7 @@ return { base = base, loop = loop, event = event, + event_base = base, addevent = newevent, addserver = addserver, addclient = addclient, -- cgit v1.2.3 From 723b03c6bfeacaa3f6d04ea0a1c719216cd01494 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 03:16:14 +0000 Subject: util.timer: Fix libevent timers (event.base doesn't exist...) --- util/timer.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/timer.lua b/util/timer.lua index 4c9a3ea9..efc1812d 100644 --- a/util/timer.lua +++ b/util/timer.lua @@ -9,6 +9,7 @@ local ns_addtimer = require "net.server".addtimer; local event = require "net.server".event; +local event_base = require "net.server".event_base; local get_time = os.time; local t_insert = table.insert; @@ -54,7 +55,7 @@ if not event then else local EVENT_LEAVE = (event.core and event.core.LEAVE) or -1; function _add_task(delay, func) - event.base:addevent(nil, event.EV_TIMEOUT, function () + event_base:addevent(nil, event.EV_TIMEOUT, function () local ret = func(); if ret then _add_task(ret, func); -- cgit v1.2.3 From 49d04cec1d78f63d0107dfb8ee7b151b0db7b552 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 13:15:46 +0000 Subject: mod_bosh: Don't log response XML --- plugins/mod_bosh.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_bosh.lua b/plugins/mod_bosh.lua index 3e41ef7b..5de79eff 100644 --- a/plugins/mod_bosh.lua +++ b/plugins/mod_bosh.lua @@ -152,7 +152,7 @@ function stream_callbacks.streamopened(request, attr) local r, send_buffer = session.requests, session.send_buffer; local response = { headers = default_headers } function session.send(s) - log("debug", "Sending BOSH data: %s", tostring(s)); + --log("debug", "Sending BOSH data: %s", tostring(s)); local oldest_request = r[1]; while oldest_request and oldest_request.destroyed do t_remove(r, 1); @@ -160,7 +160,7 @@ function stream_callbacks.streamopened(request, attr) oldest_request = r[1]; end if oldest_request then - log("debug", "We have an open request, so using that to send with"); + log("debug", "We have an open request, so sending on that"); response.body = t_concat{"", tostring(s), "" }; oldest_request:send(response); --log("debug", "Sent"); -- cgit v1.2.3 From 38dc47caaef527891f068fb666af55f41ee3cb5b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 13:47:45 +0000 Subject: s2smanager: Don't tostring() the data before sending, sends2s already does this. --- core/s2smanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 4c61eaa3..aa6de317 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -391,7 +391,7 @@ function streamopened(session, attr) if send_buffer and #send_buffer > 0 then log("debug", "Sending s2s send_buffer now..."); for i, data in ipairs(send_buffer) do - session.sends2s(tostring(data)); + session.sends2s(data); send_buffer[i] = nil; end end -- cgit v1.2.3 From c3ed7fbfced3ef8d3cb76185106bed6270104783 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 13:49:48 +0000 Subject: s2smanager: Don't log full stanza when sending outwards --- core/s2smanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index aa6de317..d8edaed0 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -128,7 +128,7 @@ function new_incoming(conn) open_sessions = open_sessions + 1; local w, log = conn.write, logger_init("s2sin"..tostring(conn):match("[a-f0-9]+$")); session.log = log; - session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end + session.sends2s = function (t) log("debug", "sending: %s", (t.top_tag and t:top_tag()) or t:match("^([^>]*>?)")); w(tostring(t)); end incoming_s2s[session] = true; add_task(connect_timeout, function () if session.conn ~= conn or @@ -317,7 +317,7 @@ function make_connect(host_session, connect_host, connect_port) cl.register_outgoing(conn, host_session); local w, log = conn.write, host_session.log; - host_session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end + host_session.sends2s = function (t) log("debug", "sending: %s", (t.top_tag and t:top_tag()) or t:match("^[^>]*>?")); w(tostring(t)); end conn.write(format([[]], from_host, to_host)); log("debug", "Connection attempt in progress..."); -- cgit v1.2.3 From ecb81f3ad2145fc28cbc81c87769cc1cf6ad61d1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:07:22 +0000 Subject: sessionmanager: Use : syntax for calling connection methods --- core/sessionmanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 08e70d44..7e609f22 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -50,8 +50,8 @@ function new_session(conn) open_sessions = open_sessions + 1; log("debug", "open sessions now: ".. open_sessions); local w = conn.write; - session.send = function (t) w(tostring(t)); end - session.ip = conn.ip(); + session.send = function (t) w(conn, tostring(t)); end + session.ip = conn:ip(); local conn_name = "c2s"..tostring(conn):match("[a-f0-9]+$"); session.log = logger.init(conn_name); -- cgit v1.2.3 From 5f0238bc36814cd6d0beefde1a3a31d39d01b5e4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:11:59 +0000 Subject: net.connlisteners: Standardise on new syntax for addserver(), and clean up a bit --- net/connlisteners.lua | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/net/connlisteners.lua b/net/connlisteners.lua index 230d92a4..a9b92a8c 100644 --- a/net/connlisteners.lua +++ b/net/connlisteners.lua @@ -61,9 +61,14 @@ function start(name, udata) end end - return server.addserver(h, - (udata and udata.port) or h.default_port or error("Can't start listener "..name.." because no port was specified, and it has no default port", 0), - (udata and udata.interface) or h.default_interface or "*", (udata and udata.mode) or h.default_mode or 1, (udata and udata.ssl) or nil, 99999999, udata and udata.type == "ssl"); + local interface = (udata and udata.interface) or h.default_interface or "*"; + local port = (udata and udata.port) or h.default_port or error("Can't start listener "..name.." because no port was specified, and it has no default port", 0); + local mode = (udata and udata.mode) or h.default_mode or 1; + local ssl = (udata and udata.ssl) or nil; + local maxclients = 99999999; + local autossl = udata and udata.type == "ssl"; + + return server.addserver(interface, port, h, mode, ssl, autossl); end return _M; -- cgit v1.2.3 From 5c624344662b7330a6f87d3b1e83865c2ac4dd62 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:13:21 +0000 Subject: net.server: Small fix for addtimer() compatibility code --- net/server.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server.lua b/net/server.lua index 507bd81e..15bba603 100644 --- a/net/server.lua +++ b/net/server.lua @@ -9,7 +9,7 @@ if have_luaevent and use_luaevent == true then -- Backwards compatibility for timers, addtimer -- called a function roughly every second - local add_task = require "util.timer"; + local add_task = require "util.timer".add_task; function server.addtimer(f) return add_task(1, function (...) f(...); return 1; end); end -- cgit v1.2.3 From 6c6f24c491d6c3df9899d7a9c2734d72bec6e67c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:14:59 +0000 Subject: xmppclient_listener: Switch to .onincoming listener method, to be compatible with new server_event.lua --- net/xmppclient_listener.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index 417dfd4a..fd4e7097 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -108,7 +108,7 @@ end -- End of session methods -- -function xmppclient.listener(conn, data) +function xmppclient.onincoming(conn, data) local session = sessions[conn]; if not session then session = sm_new_session(conn); -- cgit v1.2.3 From 2aa2de8a5bb0a37e167c4aab7cfa3a84c1444e95 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:15:59 +0000 Subject: xmppclient_listener: Use : syntax for connection methods, to be compatible with new server.lua --- net/xmppclient_listener.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index fd4e7097..34438f3d 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -100,7 +100,7 @@ local function session_close(session, reason) end end session.send(""); - session.conn.close(); + session.conn:close(); xmppclient.disconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed"); end end @@ -117,7 +117,7 @@ function xmppclient.onincoming(conn, data) session.log("info", "Client connected"); -- Client is using legacy SSL (otherwise mod_tls sets this flag) - if conn.ssl() then + if conn:ssl() then session.secure = true; end -- cgit v1.2.3 From d578bb8ff2bbdc9623622076c10c0f4236be376d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:16:46 +0000 Subject: mod_tls: Switch to : syntax for connection methods --- plugins/mod_tls.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/mod_tls.lua b/plugins/mod_tls.lua index 8a450803..706b42c9 100644 --- a/plugins/mod_tls.lua +++ b/plugins/mod_tls.lua @@ -20,9 +20,9 @@ module:add_handler("c2s_unauthed", "starttls", xmlns_starttls, session.send(st.stanza("proceed", { xmlns = xmlns_starttls })); session:reset_stream(); if session.host and hosts[session.host].ssl_ctx_in then - session.conn.set_sslctx(hosts[session.host].ssl_ctx_in); + session.conn:set_sslctx(hosts[session.host].ssl_ctx_in); end - session.conn.starttls(); + session.conn:starttls(); session.log("info", "TLS negotiation started..."); session.secure = false; else @@ -37,9 +37,9 @@ module:add_handler("s2sin_unauthed", "starttls", xmlns_starttls, session.sends2s(st.stanza("proceed", { xmlns = xmlns_starttls })); session:reset_stream(); if session.to_host and hosts[session.to_host].ssl_ctx_in then - session.conn.set_sslctx(hosts[session.to_host].ssl_ctx_in); + session.conn:set_sslctx(hosts[session.to_host].ssl_ctx_in); end - session.conn.starttls(); + session.conn:starttls(); session.log("info", "TLS negotiation started for incoming s2s..."); session.secure = false; else @@ -91,7 +91,7 @@ module:hook_stanza(xmlns_starttls, "proceed", module:log("debug", "Proceeding with TLS on s2sout..."); local format, to_host, from_host = string.format, session.to_host, session.from_host; session:reset_stream(); - session.conn.starttls(true); + session.conn:starttls(true); session.secure = false; return true; end); -- cgit v1.2.3 From f24860c37c3821c02a5ed91e706c6d4d6fd21bbc Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:19:57 +0000 Subject: net.server_select: Change addserver() parameters to be compatible with new standard syntax --- net/server_select.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/server_select.lua b/net/server_select.lua index 6ab8ce91..eebaeb81 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -692,7 +692,7 @@ end ----------------------------------// PUBLIC //-- -addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, startssl ) -- this function provides a way for other scripts to reg a server +addserver = function( addr, port, listeners, pattern, sslctx, startssl ) -- this function provides a way for other scripts to reg a server local err --out_put("server.lua: autossl on ", port, " is ", startssl) if type( listeners ) ~= "table" then @@ -715,7 +715,7 @@ addserver = function( listeners, port, addr, pattern, sslctx, maxconnections, st out_error( "server.lua, port ", port, ": ", err ) return nil, err end - local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, maxconnections, startssl ) -- wrap new server socket + local handler, err = wrapserver( listeners, server, addr, port, pattern, sslctx, _maxclientsperserver, startssl ) -- wrap new server socket if not handler then server:close( ) return nil, err -- cgit v1.2.3 From 3637339b38f22bdb048e72f22241bd2baafe7e4c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 17:23:51 +0000 Subject: net.server_event: Change to new standard addserver() syntax --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index 375ab47e..99c3c83b 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -639,9 +639,9 @@ do end local addserver = ( function( ) - return function( addr, port, listener, pattern, backlog, sslcfg, startssl ) -- TODO: check arguments + return function( addr, port, listener, pattern, sslcfg, startssl ) -- TODO: check arguments --vdebug( "creating new tcp server with following parameters:", addr or "nil", port or "nil", sslcfg or "nil", startssl or "nil") - local server, err = socket.bind( addr, port, backlog ) -- create server socket + local server, err = socket.bind( addr, port, cfg.ACCEPT_QUEUE ) -- create server socket if not server then debug( "creating server socket failed because:", err ) return nil, err -- cgit v1.2.3 From 2b75fe3807dcaa4aeafbe8adb864c8540933e8e3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 21 Nov 2009 23:04:26 +0000 Subject: objectmanager: Convert to unix line-endings --- core/objectmanager.lua | 120 ++++++++++++++++++++++++------------------------- 1 file changed, 60 insertions(+), 60 deletions(-) diff --git a/core/objectmanager.lua b/core/objectmanager.lua index e96cbd90..293622c9 100644 --- a/core/objectmanager.lua +++ b/core/objectmanager.lua @@ -6,63 +6,63 @@ -- COPYING file in the source package for more information. -- - -local new_multitable = require "util.multitable".new; -local t_insert = table.insert; -local t_concat = table.concat; -local tostring = tostring; -local unpack = unpack; -local pairs = pairs; -local error = error; -local type = type; -local _G = _G; - -local data = new_multitable(); - -module "objectmanager" - -function set(...) - return data:set(...); -end -function remove(...) - return data:remove(...); -end -function get(...) - return data:get(...); -end - -local function get_path(path) - if type(path) == "table" then return path; end - local s = {}; - for part in tostring(path):gmatch("[%w_]+") do - t_insert(s, part); - end - return s; -end - -function get_object(path) - path = get_path(path) - return data:get(unpack(path)), path; -end -function set_object(path, object) - path = get_path(path); - data:set(unpack(path), object); -end - -data:set("ls", function(_dir) - local obj, dir = get_object(_dir); - if not obj then error("object not found: " .. t_concat(dir, '/')); end - local r = {}; - if type(obj) == "table" then - for key, val in pairs(obj) do - r[key] = type(val); - end - end - return r; -end); -data:set("get", get_object); -data:set("set", set_object); -data:set("echo", function(...) return {...}; end); -data:set("_G", _G); - -return _M; + +local new_multitable = require "util.multitable".new; +local t_insert = table.insert; +local t_concat = table.concat; +local tostring = tostring; +local unpack = unpack; +local pairs = pairs; +local error = error; +local type = type; +local _G = _G; + +local data = new_multitable(); + +module "objectmanager" + +function set(...) + return data:set(...); +end +function remove(...) + return data:remove(...); +end +function get(...) + return data:get(...); +end + +local function get_path(path) + if type(path) == "table" then return path; end + local s = {}; + for part in tostring(path):gmatch("[%w_]+") do + t_insert(s, part); + end + return s; +end + +function get_object(path) + path = get_path(path) + return data:get(unpack(path)), path; +end +function set_object(path, object) + path = get_path(path); + data:set(unpack(path), object); +end + +data:set("ls", function(_dir) + local obj, dir = get_object(_dir); + if not obj then error("object not found: " .. t_concat(dir, '/')); end + local r = {}; + if type(obj) == "table" then + for key, val in pairs(obj) do + r[key] = type(val); + end + end + return r; +end); +data:set("get", get_object); +data:set("set", set_object); +data:set("echo", function(...) return {...}; end); +data:set("_G", _G); + +return _M; -- cgit v1.2.3 From d1205508582c22acda941e3853dc8ef049341f92 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:14:32 +0000 Subject: net.server_event: Define vdebug function for convenience --- net/server_event.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/server_event.lua b/net/server_event.lua index 99c3c83b..453a1f28 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -52,6 +52,7 @@ local log = require ("util.logger").init("socket") local function debug(...) return log("debug", ("%s "):rep(select('#', ...)), ...) end +local vdebug = debug; local bitor = ( function( ) -- thx Rici Lake local hasbit = function( x, p ) -- cgit v1.2.3 From 920c067b7ffe42f6a18a0561191f9bbc523a8864 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:20:35 +0000 Subject: net.server_event: Define id property for connection objects, to aid logging --- net/server_event.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/server_event.lua b/net/server_event.lua index 453a1f28..60ff5b72 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -444,6 +444,7 @@ do _sslctx = sslctx; -- parameters _usingssl = false; -- client is using ssl; } + interface.id = tostring(interface):match("%x+$"); interface.writecallback = function( event ) -- called on write events --vdebug( "new client write event, id/ip/port:", interface, ip, port ) if interface.nowriting or ( interface.fatalerror and ( "client to close" ~= interface.fatalerror ) ) then -- leave this event @@ -592,6 +593,7 @@ do fatalerror = false; -- error message nointerface = true; -- lock/unlock parameter } + interface.id = tostring(interface):match("%x+$"); interface.readcallback = function( event ) -- server handler, called on incoming connections --vdebug( "server can accept, id/addr/port:", interface, addr, port ) if interface.fatalerror then -- cgit v1.2.3 From 08ea9601ce7890df559dd8a6fd2cacce432addda Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:42:45 +0000 Subject: net.server_event: Use connection id in all relevant logging --- net/server_event.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index 60ff5b72..23a8ff86 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -140,14 +140,14 @@ do self.fatalerror = "connection timeout" self.listener.ontimeout( self ) -- call timeout listener self:_close() - debug( "new connection failed. id:", self, "error:", self.fatalerror ) + debug( "new connection failed. id:", self.id, "error:", self.fatalerror ) else if plainssl then -- start ssl session self:_start_ssl( self.listener.onconnect ) else -- normal connection self:_start_session( self.listener.onconnect ) end - debug( "new connection established. id:", self ) + debug( "new connection established. id:", self.id ) end self.eventconnect = nil return -1 @@ -159,7 +159,7 @@ do if self.type == "client" then local callback = function( ) self:_lock( false, false, false ) - --vdebug( "start listening on client socket with id:", self ) + --vdebug( "start listening on client socket with id:", self.id ) self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT ) -- register callback onconnect( self ) self.eventsession = nil @@ -168,13 +168,13 @@ do self.eventsession = addevent( base, nil, EV_TIMEOUT, callback, 0 ) else self:_lock( false ) - --vdebug( "start listening on server socket with id:", self ) + --vdebug( "start listening on server socket with id:", self.id ) self.eventread = addevent( base, self.conn, EV_READ, self.readcallback ) -- register callback end return true end function interface_mt:_start_ssl(arg) -- old socket will be destroyed, therefore we have to close read/write events first - --vdebug( "starting ssl session with client id:", self ) + --vdebug( "starting ssl session with client id:", self.id ) local _ _ = self.eventread and self.eventread:close( ) -- close events; this must be called outside of the event callbacks! _ = self.eventwrite and self.eventwrite:close( ) @@ -199,7 +199,7 @@ do local maxattempt = cfg.MAX_HANDSHAKE_ATTEMPS while attempt < 1000 do -- no endless loop attempt = attempt + 1 - debug( "ssl handshake of client with id:", self, "attemp:", attempt ) + debug( "ssl handshake of client with id:"..tostring(self).."attemp:"..attempt ) if attempt > maxattempt then self.fatalerror = "max handshake attemps exceeded" elseif EV_TIMEOUT == event then @@ -249,7 +249,7 @@ do return true end function interface_mt:_destroy() -- close this interface + events and call last listener - debug( "closing client with id:", self ) + debug( "closing client with id:", self.id ) self:_lock( true, true, true ) -- first of all, lock the interface to avoid further actions local _ _ = self.eventread and self.eventread:close( ) -- close events; this must be called outside of the event callbacks! @@ -289,7 +289,7 @@ do -- Public methods function interface_mt:write(data) - --vdebug( "try to send data to client, id/data:", self, data ) + vdebug( "try to send data to client, id/data:", self.id, data ) data = tostring( data ) local len = string_len( data ) local total = len + self.writebufferlen @@ -307,7 +307,7 @@ do return true end function interface_mt:close(now) - debug( "try to close client connection with id:", self ) + debug( "try to close client connection with id:", self.id ) if self.type == "client" then self.fatalerror = "client to close" if ( not self.eventwrite ) or now then -- try to close immediately @@ -320,7 +320,7 @@ do return nil, "writebuffer not empty, waiting" end else - debug( "try to close server with id:", self, "args:", now ) + debug( "try to close server with id:", self.id, "args:", now ) self.fatalerror = "server to close" self:_lock( true ) local count = 0 @@ -372,7 +372,7 @@ do function interface_mt:starttls() - debug( "try to start ssl at client id:", self ) + debug( "try to start ssl at client id:", self.id ) local err if not self.sslctx then -- no ssl available err = "no ssl context available" -- cgit v1.2.3 From 7ae00da04478cee9fe34bde8cc016292ce2a503d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:44:15 +0000 Subject: net.server_event: Call connection's onconnect, no need to pass closures around now --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index 23a8ff86..879d9801 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -161,7 +161,7 @@ do self:_lock( false, false, false ) --vdebug( "start listening on client socket with id:", self.id ) self.eventread = addevent( base, self.conn, EV_READ, self.readcallback, cfg.READ_TIMEOUT ) -- register callback - onconnect( self ) + self:onconnect() self.eventsession = nil return -1 end -- cgit v1.2.3 From 14cd16dda7f1b5fb53f5a88edf4bb815a47b0fde Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:48:04 +0000 Subject: net.server_event: Correct a few instances of .sslctx to ._sslctx --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index 879d9801..88292e35 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -180,7 +180,7 @@ do _ = self.eventwrite and self.eventwrite:close( ) self.eventread, self.eventwrite = nil, nil local err - self.conn, err = ssl.wrap( self.conn, self.sslctx ) + self.conn, err = ssl.wrap( self.conn, self._sslctx ) if err then self.fatalerror = err self.conn = nil -- cannot be used anymore @@ -374,7 +374,7 @@ do function interface_mt:starttls() debug( "try to start ssl at client id:", self.id ) local err - if not self.sslctx then -- no ssl available + if not self._sslctx then -- no ssl available err = "no ssl context available" elseif self.usingssl then -- startssl was already called err = "ssl already active" -- cgit v1.2.3 From 9cb25f6151d046c5eba18c2c6af207a13906e3da Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:49:47 +0000 Subject: net.server_event: Small whitespace fixes --- net/server_event.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index 88292e35..c35c6f53 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -221,7 +221,7 @@ do self.eventhandshake = nil return -1 end - debug( "error during ssl handshake:", err ) + debug( "error during ssl handshake:", err ) if err == "wantwrite" then event = EV_WRITE elseif err == "wantread" then @@ -275,6 +275,7 @@ do interfacelist( "delete", self ) return true end + function interface_mt:_lock(nointerface, noreading, nowriting) -- lock or unlock this interface or events self.nointerface, self.noreading, self.nowriting = nointerface, noreading, nowriting return nointerface, noreading, nowriting -- cgit v1.2.3 From d506a991daf0c2cd0dac67fd95fa3f6297eeeb0d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:51:31 +0000 Subject: net.server_event: Use correct listener callbacks --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index c35c6f53..456db8b8 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -212,9 +212,9 @@ do self.receive = self.conn.receive local onsomething if "onconnect" == arg then -- trigger listener - onsomething = self.listener.onconnect + onsomething = self.onconnect else - onsomething = self.listener.onsslconnection + onsomething = self.onsslconnection end self:_start_session( onsomething ) debug( "ssl handshake done" ) -- cgit v1.2.3 From 9ad7e92bf95639ba57df4811b44ba714c6f2402f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:53:07 +0000 Subject: net.server_event: conn.usingssl -> conn._usingssl --- net/server_event.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index 456db8b8..1a77953d 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -355,7 +355,7 @@ do end function interface_mt:ssl() - return self.usingssl + return self._usingssl end function interface_mt:type() @@ -377,14 +377,14 @@ do local err if not self._sslctx then -- no ssl available err = "no ssl context available" - elseif self.usingssl then -- startssl was already called + elseif self._usingssl then -- startssl was already called err = "ssl already active" end if err then debug( "error:", err ) return nil, err end - self.usingssl = true + self._usingssl = true self.startsslcallback = function( ) -- we have to start the handshake outside of a read/write event self:_start_ssl(); self.eventstarthandshake = nil @@ -460,7 +460,7 @@ do interface.eventwrite = false return -1 else -- can write :) - if interface.usingssl then -- handle luasec + if interface._usingssl then -- handle luasec if interface.eventreadtimeout then -- we have to read first local ret = interface.readcallback( ) -- call readcallback --vdebug( "tried to read in writecallback, result:", ret ) -- cgit v1.2.3 From e6249e1a9e5f4e1cf2cc36d46402d4a529982cd0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:53:44 +0000 Subject: net.server_event: Add set_sslctx() method to connections --- net/server_event.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index 1a77953d..41c5a02b 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -370,7 +370,9 @@ do return self.addr end - + function interface_mt:set_sslctx(sslctx) + self._sslctx = sslctx; + end function interface_mt:starttls() debug( "try to start ssl at client id:", self.id ) -- cgit v1.2.3 From e2d6a0c9c733449be39f5d728b4122eed428d766 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:55:48 +0000 Subject: net.server_event: Set startsslcallback property to nil when we're done handshaking, otherwise we keep trying to repeat the handshake --- net/server_event.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/net/server_event.lua b/net/server_event.lua index 41c5a02b..50538e05 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -388,6 +388,7 @@ do end self._usingssl = true self.startsslcallback = function( ) -- we have to start the handshake outside of a read/write event + self.startsslcallback = nil self:_start_ssl(); self.eventstarthandshake = nil return -1 -- cgit v1.2.3 From 4d67d89061c92629e6e942ab2851482cc077ad7d Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 03:58:37 +0000 Subject: net.server_event: Use conn property for send/receive instead of caching, fixes trying to use original socket after SSL-wrapping --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index 50538e05..75c146eb 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -473,7 +473,7 @@ do interface.eventwritetimeout = false end end - local succ, err, byte = interface.send( interface.conn, interface.writebuffer, 1, interface.writebufferlen ) + local succ, err, byte = interface.conn:send( interface.writebuffer, 1, interface.writebufferlen ) --vdebug( "write data:", interface.writebuffer, "error:", err, "part:", byte ) if succ then -- writing succesful interface.writebuffer = "" @@ -539,8 +539,8 @@ do interface.eventreadtimeout = nil end end - local buffer, err, part = receive( client, pattern ) -- receive buffer with "pattern" --vdebug( "read data:", buffer, "error:", err, "part:", part ) + local buffer, err, part = interface.conn:receive( pattern ) -- receive buffer with "pattern" buffer = buffer or part or "" local len = string_len( buffer ) if len > cfg.MAX_READ_LENGTH then -- check buffer length -- cgit v1.2.3 From 3fe8e5575403a0e9a06923dfcf62773c40cd1ec3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:04:04 +0000 Subject: net.server_event: Obey nowriting/nointerface for locks in interface methods --- net/server_event.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/server_event.lua b/net/server_event.lua index 75c146eb..e4653cb1 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -290,6 +290,7 @@ do -- Public methods function interface_mt:write(data) + if self.nowriting then return nil, "locked" end vdebug( "try to send data to client, id/data:", self.id, data ) data = tostring( data ) local len = string_len( data ) @@ -308,6 +309,7 @@ do return true end function interface_mt:close(now) + if self.nointerface then return nil, "locked"; end debug( "try to close client connection with id:", self.id ) if self.type == "client" then self.fatalerror = "client to close" -- cgit v1.2.3 From 07f5e5f4c81bfaaa9389bcb15302349de388c966 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:06:00 +0000 Subject: net.server_event: Fix moved line caused by Mercurial's crecord and my selective commits --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index e4653cb1..5bff633d 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -541,8 +541,8 @@ do interface.eventreadtimeout = nil end end - --vdebug( "read data:", buffer, "error:", err, "part:", part ) local buffer, err, part = interface.conn:receive( pattern ) -- receive buffer with "pattern" + --vdebug( "read data:", tostring(buffer), "error:", tostring(err), "part:", tostring(part) ) buffer = buffer or part or "" local len = string_len( buffer ) if len > cfg.MAX_READ_LENGTH then -- check buffer length -- cgit v1.2.3 From 26277a79500975351326541d321a8d4ad73e9e48 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:06:40 +0000 Subject: net.server_event: Remove premature non-optimisation --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index 5bff633d..5df764b4 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -516,7 +516,7 @@ do end end end - local usingssl, receive = interface._usingssl, interface.receive; + interface.readcallback = function( event ) -- called on read events --vdebug( "new client read event, id/ip/port:", interface, ip, port ) if interface.noreading or interface.fatalerror then -- leave this event @@ -531,7 +531,7 @@ do interface.eventread = nil return -1 else -- can read - if usingssl then -- handle luasec + if interface._usingssl then -- handle luasec if interface.eventwritetimeout then -- ok, in the past writecallback was regged local ret = interface.writecallback( ) -- call it --vdebug( "tried to write in readcallback, result:", ret ) -- cgit v1.2.3 From 0f840b5fe7bef3b3ebc9529ebb90c55f802eb43b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:08:11 +0000 Subject: net.server_event: Handle and ignore 'wantread' error from receive(), treat as a timeout --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index 5df764b4..82ad899f 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -552,7 +552,7 @@ do interface.eventread = nil return -1 end - if err and ( "timeout" ~= err ) then + if err and ( err ~= "timeout" and err ~= "wantread" ) then if "wantwrite" == err then -- need to read on write event if not interface.eventwrite then -- register new write event if needed interface.eventwrite = addevent( base, interface.conn, EV_WRITE, interface.writecallback, cfg.WRITE_TIMEOUT ) -- cgit v1.2.3 From 2b683fc8e66bd737c56635e516c65e502d879d13 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:09:57 +0000 Subject: net.server_event: Comment accidentally uncommented vdebug() --- net/server_event.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index 82ad899f..b467a84d 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -291,7 +291,7 @@ do -- Public methods function interface_mt:write(data) if self.nowriting then return nil, "locked" end - vdebug( "try to send data to client, id/data:", self.id, data ) + --vdebug( "try to send data to client, id/data:", self.id, data ) data = tostring( data ) local len = string_len( data ) local total = len + self.writebufferlen -- cgit v1.2.3 From 8da42f8d71b94aacf7b914bd9c1d1147cb33e041 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:42:03 +0000 Subject: s2smanager: Update for new net.server API (s2s still doesn't work with libevent for other reasons) --- core/s2smanager.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 4c61eaa3..5dae58c9 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -128,7 +128,7 @@ function new_incoming(conn) open_sessions = open_sessions + 1; local w, log = conn.write, logger_init("s2sin"..tostring(conn):match("[a-f0-9]+$")); session.log = log; - session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end + session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(conn, tostring(t)); end incoming_s2s[session] = true; add_task(connect_timeout, function () if session.conn ~= conn or @@ -317,9 +317,9 @@ function make_connect(host_session, connect_host, connect_port) cl.register_outgoing(conn, host_session); local w, log = conn.write, host_session.log; - host_session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(tostring(t)); end + host_session.sends2s = function (t) log("debug", "sending: %s", tostring(t)); w(conn, tostring(t)); end - conn.write(format([[]], from_host, to_host)); + conn:write(format([[]], from_host, to_host)); log("debug", "Connection attempt in progress..."); add_task(connect_timeout, function () if host_session.conn ~= conn or -- cgit v1.2.3 From 8f2a8c9c0935ad80f10308838667295cbedcf080 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:42:47 +0000 Subject: net.adns: Update for new net.server API (doesn't work with libevent yet) --- net/adns.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/adns.lua b/net/adns.lua index b0c9a625..c9cb9476 100644 --- a/net/adns.lua +++ b/net/adns.lua @@ -45,10 +45,10 @@ end function new_async_socket(sock, resolver) local newconn, peername = {}, ""; local listener = {}; - function listener.incoming(conn, data) + function listener.onincoming(conn, data) dns.feed(sock, data); end - function listener.disconnect(conn, err) + function listener.ondisconnect(conn, err) log("warn", "DNS socket for %s disconnected: %s", peername, err); local servers = resolver.server; if resolver.socketset[newconn.handler] == resolver.best_server and resolver.best_server == #servers then @@ -68,7 +68,7 @@ function new_async_socket(sock, resolver) newconn.handler.setsockname = function (_, ...) return sock:setsockname(...); end newconn.handler.setpeername = function (_, ...) peername = (...); local ret = sock:setpeername(...); _.setsend(sock.send); return ret; end newconn.handler.connect = function (_, ...) return sock:connect(...) end - newconn.handler.send = function (_, data) _.write(data); return _.sendbuffer(); end + newconn.handler.send = function (_, data) _.write(_, data); return _.sendbuffer(); end return newconn.handler; end -- cgit v1.2.3 From 31dd61128403f0af9e2482856ec95d18c85f79d5 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:43:46 +0000 Subject: *_listener: Update for new net.server API, specifically .listener -> .onincoming, .disconnect -> .ondisconnect --- net/httpclient_listener.lua | 4 ++-- net/httpserver_listener.lua | 4 ++-- net/xmppclient_listener.lua | 4 ++-- net/xmppcomponent_listener.lua | 6 +++--- net/xmppserver_listener.lua | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/net/httpclient_listener.lua b/net/httpclient_listener.lua index 69b7946b..6517de44 100644 --- a/net/httpclient_listener.lua +++ b/net/httpclient_listener.lua @@ -15,7 +15,7 @@ local buffers = {}; -- Buffers of partial lines local httpclient = { default_port = 80, default_mode = "*a" }; -function httpclient.listener(conn, data) +function httpclient.onincoming(conn, data) local request = requests[conn]; if not request then @@ -28,7 +28,7 @@ function httpclient.listener(conn, data) end end -function httpclient.disconnect(conn, err) +function httpclient.ondisconnect(conn, err) local request = requests[conn]; if request then request:reader(nil); diff --git a/net/httpserver_listener.lua b/net/httpserver_listener.lua index 455191fb..5a261a43 100644 --- a/net/httpserver_listener.lua +++ b/net/httpserver_listener.lua @@ -16,7 +16,7 @@ local requests = {}; -- Open requests local httpserver = { default_port = 80, default_mode = "*a" }; -function httpserver.listener(conn, data) +function httpserver.onincoming(conn, data) local request = requests[conn]; if not request then @@ -34,7 +34,7 @@ function httpserver.listener(conn, data) end end -function httpserver.disconnect(conn, err) +function httpserver.ondisconnect(conn, err) local request = requests[conn]; if request and not request.destroyed then request.conn = nil; diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index 34438f3d..fc0dce2c 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -101,7 +101,7 @@ local function session_close(session, reason) end session.send(""); session.conn:close(); - xmppclient.disconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed"); + xmppclient.ondisconnect(session.conn, (reason and (reason.text or reason.condition)) or reason or "session closed"); end end @@ -133,7 +133,7 @@ function xmppclient.onincoming(conn, data) end end -function xmppclient.disconnect(conn, err) +function xmppclient.ondisconnect(conn, err) local session = sessions[conn]; if session then (session.log or log)("info", "Client disconnected: %s", err); diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua index c16f41a0..b353b1b3 100644 --- a/net/xmppcomponent_listener.lua +++ b/net/xmppcomponent_listener.lua @@ -118,12 +118,12 @@ local function session_close(session, reason) end session.send(""); session.conn.close(); - component_listener.disconnect(session.conn, "stream error"); + component_listener.ondisconnect(session.conn, "stream error"); end end --- Component connlistener -function component_listener.listener(conn, data) +function component_listener.onincoming(conn, data) local session = sessions[conn]; if not session then local _send = conn.write; @@ -157,7 +157,7 @@ function component_listener.listener(conn, data) end end -function component_listener.disconnect(conn, err) +function component_listener.ondisconnect(conn, err) local session = sessions[conn]; if session then (session.log or log)("info", "component disconnected: %s (%s)", tostring(session.host), tostring(err)); diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index c7e02ec5..797ef183 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -104,14 +104,14 @@ local function session_close(session, reason) session.conn.close(true); -- Force FIXME: timer? end session.conn.close(); - xmppserver.disconnect(session.conn, "stream error"); + xmppserver.ondisconnect(session.conn, "stream error"); end end -- End of session methods -- -function xmppserver.listener(conn, data) +function xmppserver.onincoming(conn, data) local session = sessions[conn]; if not session then session = s2s_new_incoming(conn); @@ -148,7 +148,7 @@ function xmppserver.status(conn, status) end end -function xmppserver.disconnect(conn, err) +function xmppserver.ondisconnect(conn, err) local session = sessions[conn]; if session then if err and err ~= "closed" and session.srv_hosts then -- cgit v1.2.3 From 54d5d4f69dd41f033163828c24424a4e4ff8d25b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:44:26 +0000 Subject: net.httpserver, net.http: Update for new net.server API (untested) --- net/http.lua | 4 ++-- net/httpserver.lua | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/net/http.lua b/net/http.lua index 9d2f9b96..34ec11b4 100644 --- a/net/http.lua +++ b/net/http.lua @@ -152,7 +152,7 @@ function request(u, ex, callback) end req.handler, req.conn = server.wrapclient(socket.tcp(), req.host, req.port or 80, listener, "*a"); - req.write = req.handler.write; + req.write = function (...) return req.handler:write(...); end req.conn:settimeout(0); local ok, err = req.conn:connect(req.host, req.port or 80); if not ok and err ~= "timeout" then @@ -200,7 +200,7 @@ end function destroy_request(request) if request.conn then request.handler.close() - listener.disconnect(request.conn, "closed"); + listener.ondisconnect(request.conn, "closed"); end end diff --git a/net/httpserver.lua b/net/httpserver.lua index ddb4475c..51dca166 100644 --- a/net/httpserver.lua +++ b/net/httpserver.lua @@ -209,7 +209,7 @@ end function new_request(handler) return { handler = handler, conn = handler.socket, - write = handler.write, state = "request", + write = function (...) return handler:write(...); end, state = "request", server = http_servers[handler.serverport()], send = send_response, destroy = destroy_request, @@ -230,7 +230,7 @@ function destroy_request(request) end request.handler.close() if request.conn then - listener.disconnect(request.handler, "closed"); + listener.ondisconnect(request.handler, "closed"); end end end -- cgit v1.2.3 From 24dba04dcbd28501a568528f287a577036fe863e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:45:13 +0000 Subject: net.server_select: Bring up to date to new common connection API --- net/server_select.lua | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/net/server_select.lua b/net/server_select.lua index eebaeb81..d7970296 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -166,7 +166,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxco local connections = 0 - local dispatch, disconnect = listeners.incoming or listeners.listener, listeners.disconnect + local dispatch, disconnect = listeners.onincoming, listeners.ondisconnect local err @@ -241,7 +241,7 @@ wrapserver = function( listeners, socket, ip, serverport, pattern, sslctx, maxco for _, handler in pairs( _socketlist ) do if handler.serverport == serverport then handler.disconnect( handler, "server closed" ) - handler.close( true ) + handler:close( true ) end end socket:close( ) @@ -300,9 +300,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport local ssl - local dispatch = listeners.incoming or listeners.listener + local dispatch = listeners.onincoming local status = listeners.status - local disconnect = listeners.disconnect + local disconnect = listeners.ondisconnect local bufferqueue = { } -- buffer array local bufferqueuelen = 0 -- end of buffer array @@ -331,9 +331,9 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.disconnect = function( ) return disconnect end - handler.setlistener = function( listeners ) - dispatch = listeners.incoming - disconnect = listeners.disconnect + handler.setlistener = function( self, listeners ) + dispatch = listeners.onincoming + disconnect = listeners.ondisconnect end handler.getstats = function( ) return readtraffic, sendtraffic @@ -400,7 +400,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.clientport = function( ) return clientport end - local write = function( data ) + local write = function( self, data ) bufferlen = bufferlen + string_len( data ) if bufferlen > maxsendlen then _closelist[ handler ] = "send buffer exceeded" -- cannot close the client at the moment, have to wait to the end of the cycle @@ -417,26 +417,26 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport return true end handler.write = write - handler.bufferqueue = function( ) + handler.bufferqueue = function( self ) return bufferqueue end - handler.socket = function( ) + handler.socket = function( self ) return socket end - handler.pattern = function( new ) + handler.pattern = function( self, new ) pattern = new or pattern return pattern end - handler.setsend = function ( newsend ) + handler.setsend = function ( self, newsend ) send = newsend or send return send end - handler.bufferlen = function( readlen, sendlen ) + handler.bufferlen = function( self, readlen, sendlen ) maxsendlen = sendlen or maxsendlen maxreadlen = readlen or maxreadlen return maxreadlen, maxsendlen end - handler.lock = function( switch ) + handler.lock = function( self, switch ) if switch == true then handler.write = idfalse local tmp = _sendlistlen @@ -507,7 +507,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport bufferqueuelen = 0 bufferlen = 0 _sendlistlen = removesocket( _sendlist, socket, _sendlistlen ) -- delete socket from writelist - _ = needtls and handler.starttls(true) + _ = needtls and handler:starttls(true) _writetimes[ handler ] = nil _ = toclose and handler.close( ) return true @@ -529,7 +529,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport -- Set the sslctx local handshake; - function handler.set_sslctx(new_sslctx) + function handler.set_sslctx(self, new_sslctx) ssl = true sslctx = new_sslctx; local wrote @@ -564,13 +564,13 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport end end disconnect( handler, "ssl handshake failed" ) - _ = handler and handler.close( true ) -- forced disconnect + _ = handler and handler:close( true ) -- forced disconnect return false -- handshake failed end ) end if sslctx then -- ssl? - handler.set_sslctx(sslctx); + handler:set_sslctx(sslctx); if startssl then -- ssl now? --out_put("server.lua: ", "starting ssl handshake") local err @@ -590,7 +590,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport else -- We're not automatically doing SSL, so we're not secure (yet) ssl = false - handler.starttls = function( now ) + handler.starttls = function( self, now ) if not now then --out_put "server.lua: we need to do tls, but delaying until later" needtls = true @@ -737,14 +737,14 @@ removeserver = function( port ) if not handler then return nil, "no server found on port '" .. tostring( port ) .. "'" end - handler.close( ) + handler:close( ) _server[ port ] = nil return true end closeall = function( ) for _, handler in pairs( _socketlist ) do - handler.close( ) + handler:close( ) _socketlist[ _ ] = nil end _readlistlen = 0 @@ -822,7 +822,7 @@ loop = function( ) -- this is the main loop of the program end for handler, err in pairs( _closelist ) do handler.disconnect( )( handler, err ) - handler.close( true ) -- forced disconnect + handler:close( true ) -- forced disconnect end clean( _closelist ) _currenttime = os_time( ) @@ -880,14 +880,14 @@ addtimer( function( ) if os_difftime( _currenttime - timestamp ) > _sendtimeout then --_writetimes[ handler ] = nil handler.disconnect( )( handler, "send timeout" ) - handler.close( true ) -- forced disconnect + handler:close( true ) -- forced disconnect end end for handler, timestamp in pairs( _readtimes ) do if os_difftime( _currenttime - timestamp ) > _readtimeout then --_readtimes[ handler ] = nil handler.disconnect( )( handler, "read timeout" ) - handler.close( ) -- forced disconnect? + handler:close( ) -- forced disconnect? end end end -- cgit v1.2.3 From 87edc64c11a5f38229c915f32165bccd2de65a84 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:46:48 +0000 Subject: stanza_router: Don't log full stanzas destined for s2s --- core/stanza_router.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/stanza_router.lua b/core/stanza_router.lua index 00c37ed7..ad312b85 100644 --- a/core/stanza_router.lua +++ b/core/stanza_router.lua @@ -180,7 +180,7 @@ function core_route_stanza(origin, stanza) local xmlns = stanza.attr.xmlns; --stanza.attr.xmlns = "jabber:server"; stanza.attr.xmlns = nil; - log("debug", "sending s2s stanza: %s", tostring(stanza)); + log("debug", "sending s2s stanza: %s", tostring(stanza.top_tag and stanza:top_tag()) or stanza); send_s2s(origin.host, host, stanza); -- TODO handle remote routing errors stanza.attr.xmlns = xmlns; -- reset else -- cgit v1.2.3 From a1efb7e1423a9ee1e529979b098481fedce9a3a1 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 04:53:02 +0000 Subject: s2smanager: Fix syntax error introduced in merge --- core/s2smanager.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index fb532a73..e1f70693 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -128,7 +128,7 @@ function new_incoming(conn) open_sessions = open_sessions + 1; local w, log = conn.write, logger_init("s2sin"..tostring(conn):match("[a-f0-9]+$")); session.log = log; - session.sends2s = function (t) log("debug", "sending: %s", (t.top_tag and t:top_tag()) or t:match("^([^>]*>?)"); w(conn, tostring(t)); end + session.sends2s = function (t) log("debug", "sending: %s", t.top_tag and t:top_tag() or t:match("^([^>]*>?)")); w(conn, tostring(t)); end incoming_s2s[session] = true; add_task(connect_timeout, function () if session.conn ~= conn or -- cgit v1.2.3 From 92e03c5a04498c2a0e5df7314f1f10a38331ef68 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 13:47:24 +0000 Subject: xmppcomponent_listener: Use new API for writing to components, fixes traceback (thanks Tobias) --- net/xmppcomponent_listener.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua index b353b1b3..790ab0b7 100644 --- a/net/xmppcomponent_listener.lua +++ b/net/xmppcomponent_listener.lua @@ -127,7 +127,7 @@ function component_listener.onincoming(conn, data) local session = sessions[conn]; if not session then local _send = conn.write; - session = { type = "component", conn = conn, send = function (data) return _send(tostring(data)); end }; + session = { type = "component", conn = conn, send = function (data) return _send(conn, tostring(data)); end }; sessions[conn] = session; -- Logging functions -- -- cgit v1.2.3 From 61285cd82a9689e0073fabf462795aa29b58647e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 14:42:56 +0000 Subject: net.server: Add some comments to explain to waqas how it all works :) --- net/server.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/server.lua b/net/server.lua index 15bba603..a2325735 100644 --- a/net/server.lua +++ b/net/server.lua @@ -5,6 +5,9 @@ local server; if have_luaevent and use_luaevent == true then server = require "net.server_event"; + -- util.timer requires "net.server", so instead of having + -- Lua look for, and load us again (causing a loop) - set this here + -- (usually it isn't set until we return, look down there...) package.loaded["net.server"] = server; -- Backwards compatibility for timers, addtimer @@ -18,4 +21,6 @@ else package.loaded["net.server"] = server; end -return server; +-- require "net.server" shall now forever return this, +-- ie. server_select or server_event as chosen above. +return server; -- cgit v1.2.3 From 5ea819c7d8310c01f58356d80e73e42439b6bf70 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 14:58:09 +0000 Subject: mod_proxy65: Import from prosody-modules, thanks Ephraim :) --- plugins/mod_proxy65.lua | 268 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 268 insertions(+) create mode 100644 plugins/mod_proxy65.lua diff --git a/plugins/mod_proxy65.lua b/plugins/mod_proxy65.lua new file mode 100644 index 00000000..86e958d3 --- /dev/null +++ b/plugins/mod_proxy65.lua @@ -0,0 +1,268 @@ +-- Copyright (C) 2009 Thilo Cestonaro +-- +-- This project is MIT/X11 licensed. Please see the +-- COPYING file in the source package for more information. +-- +--[[ +* to restart the proxy in the console: e.g. +module:unload("proxy65"); +> server.removeserver(); +module:load("proxy65", ); +]]-- + +if module:get_host_type() ~= "component" then + error("proxy65 should be loaded as a component, please see http://prosody.im/doc/components", 0); +end + +local jid_split = require "util.jid".split; +local st = require "util.stanza"; +local componentmanager = require "core.componentmanager"; +local config_get = require "core.configmanager".get; +local connlisteners = require "net.connlisteners"; +local sha1 = require "util.hashes".sha1; + +local host, name = module:get_host(), "SOCKS5 Bytestreams Service"; +local sessions, transfers, component, replies_cache = {}, {}, nil, {}; + +local proxy_port = config_get(host, "core", "proxy65_port") or 5000; +local proxy_interface = config_get(host, "core", "proxy65_interface") or "*"; +local proxy_address = config_get(host, "core", "proxy65_address") or (proxy_interface ~= "*" and proxy_interface) or host; +local proxy_acl = config_get(host, "core", "proxy65_acl"); + +local connlistener = { default_port = proxy_port, default_interface = proxy_interface, default_mode = "*a" }; + +function connlistener.listener(conn, data) + local session = sessions[conn] or {}; + + if session.setup == nil and data ~= nil and data:sub(1):byte() == 0x05 and data:len() > 2 then + local nmethods = data:sub(2):byte(); + local methods = data:sub(3); + local supported = false; + for i=1, nmethods, 1 do + if(methods:sub(i):byte() == 0x00) then -- 0x00 == method: NO AUTH + supported = true; + break; + end + end + if(supported) then + module:log("debug", "new session found ... ") + session.setup = true; + sessions[conn] = session; + conn.write(string.char(5, 0)); + end + return; + end + if session.setup then + if session.sha ~= nil and transfers[session.sha] ~= nil then + local sha = session.sha; + if transfers[sha].activated == true and transfers[sha].initiator == conn and transfers[sha].target ~= nil then + transfers[sha].target.write(data); + return; + end + end + if data ~= nil and data:len() == 0x2F and -- 40 == length of SHA1 HASH, and 7 other bytes => 47 => 0x2F + data:sub(1):byte() == 0x05 and -- SOCKS5 has 5 in first byte + data:sub(2):byte() == 0x01 and -- CMD must be 1 + data:sub(3):byte() == 0x00 and -- RSV must be 0 + data:sub(4):byte() == 0x03 and -- ATYP must be 3 + data:sub(5):byte() == 40 and -- SHA1 HASH length must be 40 (0x28) + data:sub(-2):byte() == 0x00 and -- PORT must be 0, size 2 byte + data:sub(-1):byte() == 0x00 + then + local sha = data:sub(6, 45); -- second param is not count! it's the ending index (included!) + if transfers[sha] == nil then + transfers[sha] = {}; + transfers[sha].activated = false; + transfers[sha].target = conn; + session.sha = sha; + module:log("debug", "target connected ... "); + elseif transfers[sha].target ~= nil then + transfers[sha].initiator = conn; + session.sha = sha; + module:log("debug", "initiator connected ... "); + end + conn.write(string.char(5, 0, 0, 3, sha:len()) .. sha .. string.char(0, 0)); -- VER, REP, RSV, ATYP, BND.ADDR (sha), BND.PORT (2 Byte) + else + log:module("warn", "Neither data transfer nor initial connect of a participator of a transfer.") + conn.close(); + end + else + if data ~= nil then + module:log("warn", "unknown connection with no authentication data -> closing it"); + conn.close(); + end + end +end + +function connlistener.disconnect(conn, err) + local session = sessions[conn]; + if session then + if session.sha and transfers[session.sha] then + local initiator, target = transfers[session.sha].initiator, transfers[session.sha].target; + if initiator == conn and target ~= nil then + target.close(); + elseif target == conn and initiator ~= nil then + initiator.close(); + end + transfers[session.sha] = nil; + end + -- Clean up any session-related stuff here + sessions[conn] = nil; + end +end + +local function get_disco_info(stanza) + local reply = replies_cache.disco_info; + if reply == nil then + reply = st.iq({type='result', from=host}):query("http://jabber.org/protocol/disco#info") + :tag("identity", {category='proxy', type='bytestreams', name=name}):up() + :tag("feature", {var="http://jabber.org/protocol/bytestreams"}); + replies_cache.disco_info = reply; + end + + reply.attr.id = stanza.attr.id; + reply.attr.to = stanza.attr.from; + return reply; +end + +local function get_disco_items(stanza) + local reply = replies_cache.disco_items; + if reply == nil then + reply = st.iq({type='result', from=host}):query("http://jabber.org/protocol/disco#items"); + replies_cache.disco_items = reply; + end + + reply.attr.id = stanza.attr.id; + reply.attr.to = stanza.attr.from; + return reply; +end + +local function _jid_join(node, host, resource) + local ret = host; + if ret then + if node then + ret = node .. "@" .. ret; + end + if resource then + ret = ret .. "/" .. resource; + end + end + return ret; +end + +local function get_stream_host(origin, stanza) + local reply = replies_cache.stream_host; + local err_reply = replies_cache.stream_host_err; + local sid = stanza.tags[1].attr.sid; + local allow = false; + local jid_node, jid_host, jid_resource = jid_split(stanza.attr.from); + + if stanza.attr.from == nil then + jid_node = origin.username; + jid_host = origin.host; + jid_resource = origin.resource; + end + + if proxy_acl and #proxy_acl > 0 then + if host ~= nil then -- at least a domain is needed. + for _, acl in ipairs(proxy_acl) do + local acl_node, acl_host, acl_resource = jid_split(acl); + if ((acl_node ~= nil and acl_node == jid_node) or acl_node == nil) and + ((acl_host ~= nil and acl_host == jid_host) or acl_host == nil) and + ((acl_resource ~= nil and acl_resource == jid_resource) or acl_resource == nil) then + allow = true; + end + end + end + else + allow = true; + end + if allow == true then + if reply == nil then + reply = st.iq({type="result", from=host}) + :query("http://jabber.org/protocol/bytestreams") + :tag("streamhost", {jid=host, host=proxy_address, port=proxy_port}); + replies_cache.stream_host = reply; + end + else + module:log("warn", "Denying use of proxy for %s", tostring(_jid_join(jid_node, jid_host, jid_resource))); + if err_reply == nil then + err_reply = st.iq({type="error", from=host}) + :query("http://jabber.org/protocol/bytestreams") + :tag("error", {code='403', type='auth'}) + :tag("forbidden", {xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'}); + replies_cache.stream_host_err = err_reply; + end + reply = err_reply; + end + reply.attr.id = stanza.attr.id; + reply.attr.to = stanza.attr.from; + reply.tags[1].attr.sid = sid; + return reply; +end + +module.unload = function() + componentmanager.deregister_component(host); + connlisteners.deregister(module.host .. ':proxy65'); +end + +local function set_activation(stanza) + local from, to, sid, reply = nil; + from = stanza.attr.from; + if stanza.tags[1] ~= nil and tostring(stanza.tags[1].name) == "query" then + if stanza.tags[1].attr ~= nil then + sid = stanza.tags[1].attr.sid; + end + if stanza.tags[1].tags[1] ~= nil and tostring(stanza.tags[1].tags[1].name) == "activate" then + to = stanza.tags[1].tags[1][1]; + end + end + if from ~= nil and to ~= nil and sid ~= nil then + reply = st.iq({type="result", from=host, to=from}); + reply.attr.id = stanza.attr.id; + end + return reply, from, to, sid; +end + +function handle_to_domain(origin, stanza) + local to_node, to_host, to_resource = jid_split(stanza.attr.to); + if to_node == nil then + local type = stanza.attr.type; + if type == "error" or type == "result" then return; end + if stanza.name == "iq" and type == "get" then + local xmlns = stanza.tags[1].attr.xmlns + if xmlns == "http://jabber.org/protocol/disco#info" then + origin.send(get_disco_info(stanza)); + return true; + elseif xmlns == "http://jabber.org/protocol/disco#items" then + origin.send(get_disco_items(stanza)); + return true; + elseif xmlns == "http://jabber.org/protocol/bytestreams" then + origin.send(get_stream_host(origin, stanza)); + return true; + end + elseif stanza.name == "iq" and type == "set" then + local reply, from, to, sid = set_activation(stanza); + if reply ~= nil and from ~= nil and to ~= nil and sid ~= nil then + local sha = sha1(sid .. from .. to, true); + if transfers[sha] == nil then + module:log("error", "transfers[sha]: nil"); + elseif(transfers[sha] ~= nil and transfers[sha].initiator ~= nil and transfers[sha].target ~= nil) then + origin.send(reply); + transfers[sha].activated = true; + end + else + module:log("error", "activation failed: sid: %s, initiator: %s, target: %s", tostring(sid), tostring(from), tostring(to)); + end + end + end + return; +end + +if not connlisteners.register(module.host .. ':proxy65', connlistener) then + error("mod_proxy65: Could not establish a connection listener. Check your configuration please."); + error(" one possible cause for this would be that two proxy65 components share the same port."); +end + +connlisteners.start(module.host .. ':proxy65'); +component = componentmanager.register_component(host, handle_to_domain); -- cgit v1.2.3 From 88f3737a7f70cfeac067fd9682a22bbd86872fff Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 15:06:30 +0000 Subject: mod_proxy65: Update for new net.server API, untested --- plugins/mod_proxy65.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/mod_proxy65.lua b/plugins/mod_proxy65.lua index 86e958d3..f1314a00 100644 --- a/plugins/mod_proxy65.lua +++ b/plugins/mod_proxy65.lua @@ -48,7 +48,7 @@ function connlistener.listener(conn, data) module:log("debug", "new session found ... ") session.setup = true; sessions[conn] = session; - conn.write(string.char(5, 0)); + conn:write(string.char(5, 0)); end return; end @@ -56,7 +56,7 @@ function connlistener.listener(conn, data) if session.sha ~= nil and transfers[session.sha] ~= nil then local sha = session.sha; if transfers[sha].activated == true and transfers[sha].initiator == conn and transfers[sha].target ~= nil then - transfers[sha].target.write(data); + transfers[sha].target:write(data); return; end end @@ -81,7 +81,7 @@ function connlistener.listener(conn, data) session.sha = sha; module:log("debug", "initiator connected ... "); end - conn.write(string.char(5, 0, 0, 3, sha:len()) .. sha .. string.char(0, 0)); -- VER, REP, RSV, ATYP, BND.ADDR (sha), BND.PORT (2 Byte) + conn:write(string.char(5, 0, 0, 3, sha:len()) .. sha .. string.char(0, 0)); -- VER, REP, RSV, ATYP, BND.ADDR (sha), BND.PORT (2 Byte) else log:module("warn", "Neither data transfer nor initial connect of a participator of a transfer.") conn.close(); -- cgit v1.2.3 From b50d8bac6f81907e48aaa650311861a60abcd2ed Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 22 Nov 2009 21:33:41 +0500 Subject: loggingmanager: Explicitly flush log messages if the __FLUSH_LOG environment variable is defined (workaround for MSVCRT buffering piped output). --- core/loggingmanager.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/loggingmanager.lua b/core/loggingmanager.lua index c26fdc71..4154e1a7 100644 --- a/core/loggingmanager.lua +++ b/core/loggingmanager.lua @@ -17,6 +17,12 @@ local math_max, rep = math.max, string.rep; local os_date, os_getenv = os.date, os.getenv; local getstyle, getstring = require "util.termcolours".getstyle, require "util.termcolours".getstring; +if os.getenv("__FLUSH_LOG") then + local io_flush = io.flush; + local _io_write = io_write; + io_write = function(...) _io_write(...); io_flush(); end +end + local config = require "core.configmanager"; local eventmanager = require "core.eventmanager"; local logger = require "util.logger"; -- cgit v1.2.3 From 10d98b6eef93eff3a0c5b305aea051017e282785 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 22 Nov 2009 21:40:01 +0500 Subject: sessionmanager: Fixed and cleaned function send_to_available_resources(). The 'to' attribute for presence subscription stanzas is now preserved. --- core/sessionmanager.lua | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 08e70d44..8de70e97 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -201,22 +201,17 @@ function streamclosed(session) end function send_to_available_resources(user, host, stanza) + local jid = user.."@"..host; local count = 0; - local to = stanza.attr.to; - stanza.attr.to = nil; - local h = hosts[host]; - if h and h.type == "local" then - local u = h.sessions[user]; - if u then - for k, session in pairs(u.sessions) do - if session.presence then - session.send(stanza); - count = count + 1; - end + local user = bare_sessions[jid]; + if user then + for k, session in pairs(user.sessions) do + if session.presence then + session.send(stanza); + count = count + 1; end end end - stanza.attr.to = to; return count; end -- cgit v1.2.3 From bb00a63d236fffcfcbea919c12e8585b1c20cadb Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 22 Nov 2009 21:41:09 +0500 Subject: sessionmanager: Added function send_to_interested_resources(). --- core/sessionmanager.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 8de70e97..5e7fe06d 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -215,4 +215,19 @@ function send_to_available_resources(user, host, stanza) return count; end +function send_to_interested_resources(user, host, stanza) + local jid = user.."@"..host; + local count = 0; + local user = bare_sessions[jid]; + if user then + for k, session in pairs(user.sessions) do + if session.interested then + session.send(stanza); + count = count + 1; + end + end + end + return count; +end + return _M; -- cgit v1.2.3 From 7d63169215f0c4c0384c717980ef54ab458c5cf3 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 22 Nov 2009 21:45:31 +0500 Subject: mod_presence: Acknowledge subscription requests by responding with an unavailable presence. --- plugins/mod_presence.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/mod_presence.lua b/plugins/mod_presence.lua index f83e017b..468049cc 100644 --- a/plugins/mod_presence.lua +++ b/plugins/mod_presence.lua @@ -233,6 +233,7 @@ function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_b -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too) end else + core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- acknowledging receipt if not rostermanager.is_contact_pending_in(node, host, from_bare) then if rostermanager.set_contact_pending_in(node, host, from_bare) then sessionmanager.send_to_available_resources(node, host, stanza); -- cgit v1.2.3 From 889562f945cc57d19db944d6085fdfe5683dea5d Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 22 Nov 2009 21:47:54 +0500 Subject: mod_presence: Route incoming presence subscription stanzas (types unsubscribe, subscribed and unsubscribed) to the user before roster pushes. --- plugins/mod_presence.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/mod_presence.lua b/plugins/mod_presence.lua index 468049cc..cda8dab0 100644 --- a/plugins/mod_presence.lua +++ b/plugins/mod_presence.lua @@ -242,14 +242,17 @@ function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_b end elseif stanza.attr.type == "unsubscribe" then if rostermanager.process_inbound_unsubscribe(node, host, from_bare) then + sessionmanager.send_to_interested_resources(node, host, stanza); rostermanager.roster_push(node, host, from_bare); end elseif stanza.attr.type == "subscribed" then if rostermanager.process_inbound_subscription_approval(node, host, from_bare) then + sessionmanager.send_to_interested_resources(node, host, stanza); rostermanager.roster_push(node, host, from_bare); end elseif stanza.attr.type == "unsubscribed" then if rostermanager.process_inbound_subscription_cancellation(node, host, from_bare) then + sessionmanager.send_to_interested_resources(node, host, stanza); rostermanager.roster_push(node, host, from_bare); end end -- discard any other type -- cgit v1.2.3 From 82063a0e17822147dcb9b96fcd0a3a0bfac1c227 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sun, 22 Nov 2009 21:09:25 +0000 Subject: mod_console: Update for new net.server API --- plugins/mod_console.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/mod_console.lua b/plugins/mod_console.lua index 82045232..209981ce 100644 --- a/plugins/mod_console.lua +++ b/plugins/mod_console.lua @@ -33,7 +33,7 @@ end console = {}; function console:new_session(conn) - local w = function(s) conn.write(s:gsub("\n", "\r\n")); end; + local w = function(s) conn:write(s:gsub("\n", "\r\n")); end; local session = { conn = conn; send = function (t) w(tostring(t)); end; print = function (t) w("| "..tostring(t).."\n"); end; @@ -53,7 +53,7 @@ end local sessions = {}; -function console_listener.listener(conn, data) +function console_listener.onincoming(conn, data) local session = sessions[conn]; if not session then @@ -126,7 +126,7 @@ function console_listener.listener(conn, data) session.send(string.char(0)); end -function console_listener.disconnect(conn, err) +function console_listener.ondisconnect(conn, err) local session = sessions[conn]; if session then session.disconnect(); -- cgit v1.2.3 From a1cca576be8eeb34d63963f14785445d99bc355f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Nov 2009 02:58:42 +0000 Subject: util.dependencies: Make the commands line up properly in the "missing dependency" output. Yes, this was the commit you didn't know you were waiting for! --- util/dependencies.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/util/dependencies.lua b/util/dependencies.lua index 5b07072f..cbdecc10 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -17,8 +17,12 @@ local function missingdep(name, sources, msg) print("Prosody was unable to find "..tostring(name)); print("This package can be obtained in the following ways:"); print(""); - for k,v in pairs(sources) do - print("", k, v); + local longest_platform = 0; + for platform in pairs(sources) do + longest_platform = math.max(longest_platform, #platform); + end + for platform, source in pairs(sources) do + print("", platform..":"..(" "):rep(4+longest_platform-#platform)..source); end print(""); print(msg or (name.." is required for Prosody to run, so we will now exit.")); -- cgit v1.2.3 From 3f95c8572ce3c94b1ac4f2040ed803efbe974308 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Mon, 23 Nov 2009 08:55:27 +0500 Subject: Mainfile: Fixed some comments. --- prosody | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prosody b/prosody index ae9cd2fc..8dd37f30 100755 --- a/prosody +++ b/prosody @@ -14,7 +14,7 @@ CFG_CONFIGDIR=os.getenv("PROSODY_CFGDIR"); CFG_PLUGINDIR=os.getenv("PROSODY_PLUGINDIR"); CFG_DATADIR=os.getenv("PROSODY_DATADIR"); --- -- -- -- -- -- -- ---- -- -- -- -- -- -- -- -- +-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- if CFG_SOURCEDIR then package.path = CFG_SOURCEDIR.."/?.lua;"..package.path; @@ -82,13 +82,13 @@ function read_config() end function load_libraries() - --- Initialize logging + -- Initialize logging require "core.loggingmanager" - --- Check runtime dependencies + -- Check runtime dependencies require "util.dependencies" - --- Load socket framework + -- Load socket framework server = require "net.server" end -- cgit v1.2.3 From 8d13f90bed046737e1c005ed837ec09c95192d57 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Mon, 23 Nov 2009 19:35:24 +0500 Subject: util.serialization: Concise output for empty tables. --- util/serialization.lua | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/util/serialization.lua b/util/serialization.lua index c2bbbb8d..2a1fad02 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -13,6 +13,7 @@ local t_insert = table.insert; local t_concat = table.concat; local error = error; local pairs = pairs; +local next = next; local debug_traceback = debug.traceback; local log = require "util.logger".init("serialization"); @@ -34,21 +35,25 @@ local function _simplesave(o, ind, t, func) elseif type(o) == "string" then func(t, (("%q"):format(o):gsub("\\\n", "\\n"))); elseif type(o) == "table" then - func(t, "{\n"); - for k,v in pairs(o) do - func(t, indent(ind)); - func(t, "["); - func(t, basicSerialize(k)); - func(t, "] = "); - if ind == 0 then - _simplesave(v, 0, t, func); - else - _simplesave(v, ind+1, t, func); + if next(o) then + func(t, "{\n"); + for k,v in pairs(o) do + func(t, indent(ind)); + func(t, "["); + func(t, basicSerialize(k)); + func(t, "] = "); + if ind == 0 then + _simplesave(v, 0, t, func); + else + _simplesave(v, ind+1, t, func); + end + func(t, ",\n"); end - func(t, ",\n"); + func(t, indent(ind-1)); + func(t, "}"); + else + func(t, "{}"); end - func(t, indent(ind-1)); - func(t, "}"); elseif type(o) == "boolean" then func(t, (o and "true" or "false")); else -- cgit v1.2.3 From 5b41760cecdfd2db66b84ce92454dd61e7f1341b Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Mon, 23 Nov 2009 19:50:04 +0500 Subject: util.serialization: Replaced commas with semi-colons between table fields. --- util/serialization.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/serialization.lua b/util/serialization.lua index 2a1fad02..07a099c9 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -47,7 +47,7 @@ local function _simplesave(o, ind, t, func) else _simplesave(v, ind+1, t, func); end - func(t, ",\n"); + func(t, ";\n"); end func(t, indent(ind-1)); func(t, "}"); -- cgit v1.2.3 From 0ccdad92eff7fd98d9d4a6d3c0edbd8db21279c0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Nov 2009 16:07:33 +0000 Subject: modulemanager: New module API methods for getting config options with type conversion, get_option_string, get_option_number, get_option_boolean, get_option_array, get_option_set --- core/modulemanager.lua | 83 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 82 insertions(+), 1 deletion(-) diff --git a/core/modulemanager.lua b/core/modulemanager.lua index 9cd56187..d1f7d413 100644 --- a/core/modulemanager.lua +++ b/core/modulemanager.lua @@ -28,7 +28,9 @@ local type = type; local next = next; local rawget = rawget; local error = error; -local tostring = tostring; +local tostring, tonumber = tostring, tonumber; + +local array, set = require "util.array", require "util.set"; local autoload_modules = {"presence", "message", "iq"}; @@ -400,6 +402,85 @@ function api:get_option(name, default_value) return value; end +function api:get_option_string(...) + local value = self:get_option(...); + if type(value) == "table" then + if #value > 1 then + self:log("error", "Config option '%s' does not take a list, using just the first item", name); + end + value = value[1]; + end + if value == nil then + return nil; + end + return tostring(value); +end + +function api:get_option_number(name, ...) + local value = self:get_option(name, ...); + if type(value) == "table" then + if #value > 1 then + self:log("error", "Config option '%s' does not take a list, using just the first item", name); + end + value = value[1]; + end + local ret = tonumber(value); + if value ~= nil and ret == nil then + self:log("error", "Config option '%s' not understood, expecting a number", name); + end + return ret; +end + +function api:get_option_boolean(name, ...) + local value = self:get_option(name, ...); + if type(value) == "table" then + if #value > 1 then + self:log("error", "Config option '%s' does not take a list, using just the first item", name); + end + value = value[1]; + end + if value == nil then + return nil; + end + local ret = value == true or value == "true" or value == 1 or nil; + if ret == nil then + ret = (value == false or value == "false" or value == 0); + if ret then + ret = false; + else + ret = nil; + end + end + if ret == nil then + self:log("error", "Config option '%s' not understood, expecting true/false", name); + end + return ret; +end + +function api:get_option_array(name, ...) + local value = self:get_option(name, ...); + + if value == nil then + return nil; + end + + if type(value) ~= "table" then + return array{ value }; -- Assume any non-list is a single-item list + end + + return array():append(value); -- Clone +end + +function api:get_option_set(name, ...) + local value = self:get_option_array(name, ...); + + if value == nil then + return nil; + end + + return set.new(value); +end + local t_remove = _G.table.remove; local module_items = multitable_new(); function api:add_item(key, value) -- cgit v1.2.3 From 119d80071d6c91ed7d91237c6a7f9711adde8759 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Nov 2009 16:09:44 +0000 Subject: tests/modulemanager_option_conversion.lua: Add standalone test script for the new modulemanager config option API --- tests/modulemanager_option_conversion.lua | 55 +++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/modulemanager_option_conversion.lua diff --git a/tests/modulemanager_option_conversion.lua b/tests/modulemanager_option_conversion.lua new file mode 100644 index 00000000..7dceeaed --- /dev/null +++ b/tests/modulemanager_option_conversion.lua @@ -0,0 +1,55 @@ +package.path = "../?.lua;"..package.path; + +local api = require "core.modulemanager".api; + +local module = setmetatable({}, {__index = api}); +local opt = nil; +function module:log() end +function module:get_option(name) + if name == "opt" then + return opt; + else + return nil; + end +end + +function test_value(value, returns) + opt = value; + assert(module:get_option_number("opt") == returns.number, "number doesn't match"); + assert(module:get_option_string("opt") == returns.string, "string doesn't match"); + assert(module:get_option_boolean("opt") == returns.boolean, "boolean doesn't match"); + + if type(returns.array) == "table" then + local target_array, returned_array = returns.array, module:get_option_array("opt"); + assert(#target_array == #returned_array, "array length doesn't match"); + for i=1,#target_array do + assert(target_array[i] == returned_array[i], "array item doesn't match"); + end + else + assert(module:get_option_array("opt") == returns.array, "array is returned (not nil)"); + end + + if type(returns.set) == "table" then + local target_items, returned_items = set.new(returns.set), module:get_option_set("opt"); + assert(target_items == returned_items, "set doesn't match"); + else + assert(module:get_option_set("opt") == returns.set, "set is returned (not nil)"); + end +end + +test_value(nil, {}); + +test_value(true, { boolean = true, string = "true", array = {true}, set = {true} }); +test_value(false, { boolean = false, string = "false", array = {false}, set = {false} }); +test_value("true", { boolean = true, string = "true", array = {"true"}, set = {"true"} }); +test_value("false", { boolean = false, string = "false", array = {"false"}, set = {"false"} }); +test_value(1, { boolean = true, string = "1", array = {1}, set = {1}, number = 1 }); +test_value(0, { boolean = false, string = "0", array = {0}, set = {0}, number = 0 }); + +test_value("hello world", { string = "hello world", array = {"hello world"}, set = {"hello world"} }); +test_value(1234, { string = "1234", number = 1234, array = {1234}, set = {1234} }); + +test_value({1, 2, 3}, { boolean = true, string = "1", number = 1, array = {1, 2, 3}, set = {1, 2, 3} }); +test_value({1, 2, 3, 3, 4}, {boolean = true, string = "1", number = 1, array = {1, 2, 3, 3, 4}, set = {1, 2, 3, 4} }); +test_value({0, 1, 2, 3}, { boolean = false, string = "0", number = 0, array = {0, 1, 2, 3}, set = {0, 1, 2, 3} }); + -- cgit v1.2.3 From 7b44078ada9106c282c5ba41a75728b45ac6a65e Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Mon, 23 Nov 2009 21:46:37 +0500 Subject: prosody: Added support for command line argument '--config'. --- prosody | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/prosody b/prosody index 8dd37f30..7f69e085 100755 --- a/prosody +++ b/prosody @@ -58,7 +58,27 @@ config = require "core.configmanager" function read_config() -- TODO: Check for other formats when we add support for them -- Use lfs? Make a new conf/ dir? - local ok, level, err = config.load((CFG_CONFIGDIR or ".").."/prosody.cfg.lua"); + local filenames = {}; + + local filename; + if arg[1] == "--config" and arg[2] then + table.insert(filenames, arg[2]); + if CFG_CONFIGDIR then + table.insert(filenames, CFG_CONFIGDIR.."/"..arg[2]); + end + else + table.insert(filenames, (CFG_CONFIGDIR or ".").."/prosody.cfg.lua"); + end + for _,_filename in ipairs(filenames) do + filename = _filename; + local file = io.open(filename); + if file then + file:close(); + CFG_CONFIGDIR = filename:match("^(.*)[\\/][^\\/]*$"); + break; + end + end + local ok, level, err = config.load(filename); if not ok then print("\n"); print("**************************"); -- cgit v1.2.3 From b3b272859867ff184d622969f903ffcc60e7b906 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Nov 2009 17:31:49 +0000 Subject: util.dependencies: Add LuaFileSystem as a hard dependency --- util/dependencies.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/util/dependencies.lua b/util/dependencies.lua index 5b07072f..a0f4e453 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -41,6 +41,15 @@ if not socket then fatal = true; end +local lfs, err = softreq "lfs" +if not lfs then + missingdep("luafilesystem", { ["luarocks"] = "luarocks install luafilesystem"; + ["Ubuntu 8.04 (Hardy)"] = "sudo apt-get install liblua5.1-luafilesystem0"; + ["Source"] = "http://www.keplerproject.org/luafilesystem/"; + }); + fatal = true; +end + local ssl = softreq "ssl" if not ssl then -- cgit v1.2.3 From 86de4680024ddfcaaaf38dee06a177efb5f3d352 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Nov 2009 18:51:54 +0000 Subject: util.dependencies: Clearer message, add homepages, etc. --- util/dependencies.lua | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/util/dependencies.lua b/util/dependencies.lua index a0f4e453..6e0cfe14 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -30,23 +30,32 @@ end local lxp = softreq "lxp" if not lxp then - missingdep("luaexpat", { ["Ubuntu 8.04 (Hardy)"] = "sudo apt-get install liblua5.1-expat0"; ["luarocks"] = "luarocks install luaexpat"; }); + missingdep("luaexpat", { + ["Debian/Ubuntu"] = "sudo apt-get install liblua5.1-expat0"; + ["luarocks"] = "luarocks install luaexpat"; + ["Source"] = "http://www.keplerproject.org/luaexpat/"; + }); fatal = true; end local socket = softreq "socket" if not socket then - missingdep("luasocket", { ["Ubuntu 8.04 (Hardy)"] = "sudo apt-get install liblua5.1-socket2"; ["luarocks"] = "luarocks install luasocket"; }); + missingdep("luasocket", { + ["Ubuntu"] = "sudo apt-get install liblua5.1-socket2"; + ["luarocks"] = "luarocks install luasocket"; + ["Source"] = "http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/"; + }); fatal = true; end local lfs, err = softreq "lfs" if not lfs then - missingdep("luafilesystem", { ["luarocks"] = "luarocks install luafilesystem"; - ["Ubuntu 8.04 (Hardy)"] = "sudo apt-get install liblua5.1-luafilesystem0"; - ["Source"] = "http://www.keplerproject.org/luafilesystem/"; - }); + missingdep("luafilesystem", { + ["luarocks"] = "luarocks install luafilesystem"; + ["Debian/Ubuntu"] = "sudo apt-get install liblua5.1-luafilesystem0"; + ["Source"] = "http://www.keplerproject.org/luafilesystem/"; + }); fatal = true; end @@ -56,7 +65,11 @@ if not ssl then if config.get("*", "core", "run_without_ssl") then log("warn", "Running without SSL support because run_without_ssl is defined in the config"); else - missingdep("LuaSec", { ["Source"] = "http://www.inf.puc-rio.br/~brunoos/luasec/" }, "SSL/TLS support will not be available"); + missingdep("LuaSec", { + ["Debian/Ubuntu"] = "http://prosody.im/download/start#debian_and_ubuntu"; + ["luarocks"] = "luarocks install luasec"; + ["Source"] = "http://www.inf.puc-rio.br/~brunoos/luasec/"; + }, "SSL/TLS support will not be available"); end end -- cgit v1.2.3 From a918d40c046d0cc4576ece7799e3b8ffc69e31d4 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Nov 2009 19:59:32 +0000 Subject: util.dependencies: Missed a Debian/ --- util/dependencies.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/dependencies.lua b/util/dependencies.lua index 6e0cfe14..6decfba5 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -42,7 +42,7 @@ local socket = softreq "socket" if not socket then missingdep("luasocket", { - ["Ubuntu"] = "sudo apt-get install liblua5.1-socket2"; + ["Debian/Ubuntu"] = "sudo apt-get install liblua5.1-socket2"; ["luarocks"] = "luarocks install luasocket"; ["Source"] = "http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/"; }); -- cgit v1.2.3 From 49916630b91195f385ab4c76ee089e29da5e21a8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Mon, 23 Nov 2009 20:18:04 +0000 Subject: util.datamanager: Replace popen(mkdir) with lfs.mkdir, keeping the just-in-time creation until we have the new datamanager API --- util/datamanager.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/datamanager.lua b/util/datamanager.lua index 4d07d6cc..bfd69ebf 100644 --- a/util/datamanager.lua +++ b/util/datamanager.lua @@ -15,13 +15,13 @@ local loadfile, setfenv, pcall = loadfile, setfenv, pcall; local log = require "util.logger".init("datamanager"); local io_open = io.open; local os_remove = os.remove; -local io_popen = io.popen; local tostring, tonumber = tostring, tonumber; local error = error; local next = next; local t_insert = table.insert; local append = require "util.serialization".append; local path_separator = "/"; if os.getenv("WINDIR") then path_separator = "\\" end +local lfs_mkdir = require "lfs".mkdir; module "datamanager" @@ -43,7 +43,7 @@ local _mkdir = {}; local function mkdir(path) path = path:gsub("/", path_separator); -- TODO as an optimization, do this during path creation rather than here if not _mkdir[path] then - local x = io_popen("mkdir \""..path.."\" 2>&1"):read("*a"); + lfs_mkdir(path); _mkdir[path] = true; end return path; -- cgit v1.2.3 From 3d08f189a3c6b83515273e99d7dca72693b8971a Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 24 Nov 2009 14:37:14 +0500 Subject: net.server: Log an error when libevent is requested, but luaevent is unavailable, and don't load luaevent when not requested. --- net/server.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/server.lua b/net/server.lua index a2325735..f77b216d 100644 --- a/net/server.lua +++ b/net/server.lua @@ -1,9 +1,16 @@ -local have_luaevent = pcall(require, "luaevent.core"); + local use_luaevent = require "core.configmanager".get("*", "core", "use_libevent"); +if use_luaevent then + use_luaevent = pcall(require, "luaevent.core"); + if not use_luaevent then + log("error", "libevent not found, falling back to select()"); + end +end + local server; -if have_luaevent and use_luaevent == true then +if use_luaevent then server = require "net.server_event"; -- util.timer requires "net.server", so instead of having -- Lua look for, and load us again (causing a loop) - set this here -- cgit v1.2.3 From 4e56a3c519bc0d46a3491ca18cb58c3e8dbb5ae9 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Tue, 24 Nov 2009 22:42:08 +0500 Subject: xmppclient_listener: Escape control characters when logging invalid XML. --- net/xmppclient_listener.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index 417dfd4a..01d73a36 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -61,7 +61,7 @@ local function session_reset_stream(session) function session.data(conn, data) local ok, err = parser:parse(data); if ok then return; end - log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " ")); + log("debug", "Received invalid XML (%s) %d bytes: %s", tostring(err), #data, data:sub(1, 300):gsub("[\r\n]+", " "):gsub("[%z\1-\31]", "_")); session:close("xml-not-well-formed"); end -- cgit v1.2.3 From f5afdcb5642fb5260717613cc7909e20805a957c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Tue, 24 Nov 2009 20:34:22 +0000 Subject: core.sessionmanager, net.*_listener: Remove the evil collectgarbage() calls --- core/sessionmanager.lua | 1 - net/xmppclient_listener.lua | 1 - net/xmppcomponent_listener.lua | 1 - net/xmppserver_listener.lua | 1 - 4 files changed, 4 deletions(-) diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua index 380d8129..69160af7 100644 --- a/core/sessionmanager.lua +++ b/core/sessionmanager.lua @@ -10,7 +10,6 @@ local tonumber, tostring = tonumber, tostring; local ipairs, pairs, print, next= ipairs, pairs, print, next; -local collectgarbage = collectgarbage; local format = import("string", "format"); local hosts = hosts; diff --git a/net/xmppclient_listener.lua b/net/xmppclient_listener.lua index fc0dce2c..4fd51db6 100644 --- a/net/xmppclient_listener.lua +++ b/net/xmppclient_listener.lua @@ -140,7 +140,6 @@ function xmppclient.ondisconnect(conn, err) sm_destroy_session(session, err); sessions[conn] = nil; session = nil; - collectgarbage("collect"); end end diff --git a/net/xmppcomponent_listener.lua b/net/xmppcomponent_listener.lua index 790ab0b7..4920548d 100644 --- a/net/xmppcomponent_listener.lua +++ b/net/xmppcomponent_listener.lua @@ -169,7 +169,6 @@ function component_listener.ondisconnect(conn, err) sessions[conn] = nil; for k in pairs(session) do session[k] = nil; end session = nil; - collectgarbage("collect"); end end diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index 797ef183..3c038b83 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -162,7 +162,6 @@ function xmppserver.ondisconnect(conn, err) s2s_destroy_session(session); sessions[conn] = nil; session = nil; - collectgarbage("collect"); end end -- cgit v1.2.3 From c4742ed7810b3e10bfc6bdb55a11c31b3faf5426 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Nov 2009 03:30:00 +0000 Subject: s2smanager: Log warning when trying to send a stanza from a host we don't serve, instead of a traceback (thanks stpeter) --- core/s2smanager.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 4c61eaa3..20d932e8 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -79,6 +79,10 @@ local function bounce_sendq(session) end function send_to_host(from_host, to_host, data) + if not hosts[from_host] then + log("warn", "Attempt to send stanza from %s - a host we don't serve", from_host); + return false; + end local host = hosts[from_host].s2sout[to_host]; if host then -- We have a connection to this host already -- cgit v1.2.3 From 38e55888e5c0a041f876281cfeb6230be406c7c3 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Nov 2009 15:40:33 +0000 Subject: util.dependencies: Log an error if the current version of LuaSec installed contains The Bug (thanks Remko) --- util/dependencies.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/dependencies.lua b/util/dependencies.lua index 6decfba5..790045eb 100644 --- a/util/dependencies.lua +++ b/util/dependencies.lua @@ -71,6 +71,11 @@ if not ssl then ["Source"] = "http://www.inf.puc-rio.br/~brunoos/luasec/"; }, "SSL/TLS support will not be available"); end +else + local major, minor, veryminor, patched = ssl._VERSION:match("(%d+)%.(%d+)%.?(%d*)(M?)"); + if not major or ((tonumber(major) == 0 and (tonumber(minor) or 0) <= 3 and (tonumber(veryminor) or 0) <= 2) and patched ~= "M") then + log("error", "This version of LuaSec contains a known bug that causes disconnects, see http://prosody.im/doc/depends"); + end end local encodings, err = softreq "util.encodings" -- cgit v1.2.3 From 500ca527099ff73dc5398fac321f793d530344cf Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 25 Nov 2009 21:30:41 +0500 Subject: Removed some legacy doc/ files. --- doc/lxmppd_core_rostermanager.txt | 9 --------- doc/lxmppd_core_stanz_dispatch.txt | 27 --------------------------- 2 files changed, 36 deletions(-) delete mode 100644 doc/lxmppd_core_rostermanager.txt delete mode 100644 doc/lxmppd_core_stanz_dispatch.txt diff --git a/doc/lxmppd_core_rostermanager.txt b/doc/lxmppd_core_rostermanager.txt deleted file mode 100644 index 4f501158..00000000 --- a/doc/lxmppd_core_rostermanager.txt +++ /dev/null @@ -1,9 +0,0 @@ -lxmppd -> core -> rostermanager.lua - requires "util.datamanager" - module "rostermanager" - -function log(type, message) - logs a message of type "rostermanager" - -function getroster(username, host) - Retrieves the user's roster from the server and loads it with the datamanager \ No newline at end of file diff --git a/doc/lxmppd_core_stanz_dispatch.txt b/doc/lxmppd_core_stanz_dispatch.txt deleted file mode 100644 index 15bb730b..00000000 --- a/doc/lxmppd_core_stanz_dispatch.txt +++ /dev/null @@ -1,27 +0,0 @@ -lxmppd -> core -> stanza_dispatch - requires "util.stanza" - requires "core.usermanager" - -function init_stanza_dispatcher(session) - Initialises the stanza dispatcher which handles different stanza according - to their type and XML namespace, dispatching to required handlers. - - iq_handlers["jabber:iq:auth"] - A list of handlers for "jabber:iq:auth" stanzas -- authentication - (request) stanzas. - - function (stanza) - If one of username, password and resource are missing then it ????. - If not, then it validates the credentials and replies with the - appropriate stanza. - - iq_handlers["jabber:iq:roster"] - A list of handlers for "jabber:iq:roster" stanzas -- roster management - - function (stanza) - Parses the type of stanza for roster management and does what is - requested (roster retrieval, etc.) - - function (stanza) - Validates the stanza and calls the required handler - -- cgit v1.2.3 From d689a0035c03983aeb4fc30605d616af6855799e Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 25 Nov 2009 21:40:44 +0500 Subject: MUC: Improved handling of incoming groupchat messages (state preserved for possible later use). --- plugins/muc/muc.lib.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 3a185e17..0fb5223d 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -128,19 +128,21 @@ function room_mt:broadcast_presence(stanza, sid, code, nick) end end function room_mt:broadcast_message(stanza, historic) + local to = stanza.attr.to; for occupant, o_data in pairs(self._occupants) do for jid in pairs(o_data.sessions) do stanza.attr.to = jid; self:_route_stanza(stanza); end end + stanza.attr.to = to; if historic then -- add to history local history = self._data['history']; if not history then history = {}; self._data['history'] = history; end - -- stanza = st.clone(stanza); + stanza = st.clone(stanza); stanza:tag("delay", {xmlns = "urn:xmpp:delay", from = muc_domain, stamp = datetime.datetime()}):up(); -- XEP-0203 stanza:tag("x", {xmlns = "jabber:x:delay", from = muc_domain, stamp = datetime.legacy()}):up(); -- XEP-0091 (deprecated) - t_insert(history, st.clone(st.preserialize(stanza))); + t_insert(history, st.preserialize(stanza)); while #history > history_length do t_remove(history, 1) end end end @@ -528,6 +530,7 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha else self:broadcast_message(stanza, true); end + stanza.attr.from = from; end elseif stanza.name == "message" and type == "error" and is_kickable_error(stanza) then local current_nick = self._jid_nick[stanza.attr.from]; -- cgit v1.2.3 From 3238abd1047fdc5e1e9336177bbc80e2ea6650ae Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 25 Nov 2009 21:42:05 +0500 Subject: MUC: Prevent visitors from broadcasting messages. --- plugins/muc/muc.lib.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 0fb5223d..7907c0c3 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -519,8 +519,11 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha local from, to = stanza.attr.from, stanza.attr.to; local room = jid_bare(to); local current_nick = self._jid_nick[from]; - if not current_nick then -- not in room + local occupant = self._occupants[current_nick]; + if not occupant then -- not in room origin.send(st.error_reply(stanza, "cancel", "not-acceptable")); + elseif occupant.role == "visitor" then + origin.send(st.error_reply(stanza, "cancel", "forbidden")); else local from = stanza.attr.from; stanza.attr.from = current_nick; -- cgit v1.2.3 From bdd5151bc7c2f7d6873ca0fa2b06962567811b79 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 25 Nov 2009 22:00:33 +0500 Subject: MUC: Only allow moderators to change the room subject. --- plugins/muc/muc.lib.lua | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 7907c0c3..098fef98 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -529,7 +529,12 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha stanza.attr.from = current_nick; local subject = getText(stanza, {"subject"}); if subject then - self:set_subject(current_nick, subject); -- TODO use broadcast_message_stanza + if occupant.role == "moderator" then + self:set_subject(current_nick, subject); -- TODO use broadcast_message_stanza + else + stanza.attr.from = from; + origin.send(st.error_reply(stanza, "cancel", "forbidden")); + end else self:broadcast_message(stanza, true); end -- cgit v1.2.3 From 6aba88bc29f1f22f5c8e491dbd7bbe1b7ec1176b Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Wed, 25 Nov 2009 22:26:06 +0500 Subject: MUC: Slightly refactored form processing. --- plugins/muc/muc.lib.lua | 89 ++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 098fef98..8fd0a093 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -389,51 +389,50 @@ function room_mt:handle_to_occupant(origin, stanza) -- PM, vCards, etc end end -function room_mt:handle_form(origin, stanza) - if self:get_affiliation(stanza.attr.from) ~= "owner" then origin.send(st.error_reply(stanza, "auth", "forbidden")); return; end - if stanza.attr.type == "get" then - local title = "Configuration for "..self.jid; - origin.send(st.reply(stanza):query("http://jabber.org/protocol/muc#owner") - :tag("x", {xmlns='jabber:x:data', type='form'}) - :tag("title"):text(title):up() - :tag("instructions"):text(title):up() - :tag("field", {type='hidden', var='FORM_TYPE'}):tag("value"):text("http://jabber.org/protocol/muc#roomconfig"):up():up() - :tag("field", {type='boolean', label='Make Room Persistent?', var='muc#roomconfig_persistentroom'}) - :tag("value"):text(self._data.persistent and "1" or "0"):up() - :up() - :tag("field", {type='boolean', label='Make Room Publicly Searchable?', var='muc#roomconfig_publicroom'}) - :tag("value"):text(self._data.hidden and "0" or "1"):up() - :up() - ); - elseif stanza.attr.type == "set" then - local query = stanza.tags[1]; - local form; - for _, tag in ipairs(query.tags) do if tag.name == "x" and tag.attr.xmlns == "jabber:x:data" then form = tag; break; end end - if not form then origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); return; end - if form.attr.type == "cancel" then origin.send(st.reply(stanza)); return; end - if form.attr.type ~= "submit" then origin.send(st.error_reply(stanza, "cancel", "bad-request")); return; end - local fields = {}; - for _, field in pairs(form.tags) do - if field.name == "field" and field.attr.var and field.tags[1].name == "value" and #field.tags[1].tags == 0 then - fields[field.attr.var] = field.tags[1][1] or ""; - end +function room_mt:send_form(origin, stanza) + local title = "Configuration for "..self.jid; + origin.send(st.reply(stanza):query("http://jabber.org/protocol/muc#owner") + :tag("x", {xmlns='jabber:x:data', type='form'}) + :tag("title"):text(title):up() + :tag("instructions"):text(title):up() + :tag("field", {type='hidden', var='FORM_TYPE'}):tag("value"):text("http://jabber.org/protocol/muc#roomconfig"):up():up() + :tag("field", {type='boolean', label='Make Room Persistent?', var='muc#roomconfig_persistentroom'}) + :tag("value"):text(self._data.persistent and "1" or "0"):up() + :up() + :tag("field", {type='boolean', label='Make Room Publicly Searchable?', var='muc#roomconfig_publicroom'}) + :tag("value"):text(self._data.hidden and "0" or "1"):up() + :up() + ); +end + +function room_mt:process_form(origin, stanza) + local query = stanza.tags[1]; + local form; + for _, tag in ipairs(query.tags) do if tag.name == "x" and tag.attr.xmlns == "jabber:x:data" then form = tag; break; end end + if not form then origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); return; end + if form.attr.type == "cancel" then origin.send(st.reply(stanza)); return; end + if form.attr.type ~= "submit" then origin.send(st.error_reply(stanza, "cancel", "bad-request")); return; end + local fields = {}; + for _, field in pairs(form.tags) do + if field.name == "field" and field.attr.var and field.tags[1].name == "value" and #field.tags[1].tags == 0 then + fields[field.attr.var] = field.tags[1][1] or ""; end - if fields.FORM_TYPE ~= "http://jabber.org/protocol/muc#roomconfig" then origin.send(st.error_reply(stanza, "cancel", "bad-request")); return; end + end + if fields.FORM_TYPE ~= "http://jabber.org/protocol/muc#roomconfig" then origin.send(st.error_reply(stanza, "cancel", "bad-request")); return; end - local persistent = fields['muc#roomconfig_persistentroom']; - if persistent == "0" or persistent == "false" then persistent = nil; elseif persistent == "1" or persistent == "true" then persistent = true; - else origin.send(st.error_reply(stanza, "cancel", "bad-request")); return; end - self._data.persistent = persistent; - module:log("debug", "persistent=%s", tostring(persistent)); + local persistent = fields['muc#roomconfig_persistentroom']; + if persistent == "0" or persistent == "false" then persistent = nil; elseif persistent == "1" or persistent == "true" then persistent = true; + else origin.send(st.error_reply(stanza, "cancel", "bad-request")); return; end + self._data.persistent = persistent; + module:log("debug", "persistent=%s", tostring(persistent)); - local public = fields['muc#roomconfig_publicroom']; - if public == "0" or public == "false" then public = nil; elseif public == "1" or public == "true" then public = true; - else origin.send(st.error_reply(stanza, "cancel", "bad-request")); return; end - self._data.hidden = not public and true or nil; + local public = fields['muc#roomconfig_publicroom']; + if public == "0" or public == "false" then public = nil; elseif public == "1" or public == "true" then public = true; + else origin.send(st.error_reply(stanza, "cancel", "bad-request")); return; end + self._data.hidden = not public and true or nil; - if self.save then self:save(true); end - origin.send(st.reply(stanza)); - end + if self.save then self:save(true); end + origin.send(st.reply(stanza)); end function room_mt:handle_to_room(origin, stanza) -- presence changes and groupchat messages, along with disco/etc @@ -511,7 +510,13 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha origin.send(st.error_reply(stanza, "cancel", "bad-request")); end elseif xmlns == "http://jabber.org/protocol/muc#owner" and (type == "get" or type == "set") and stanza.tags[1].name == "query" then - self:handle_form(origin, stanza); + if self:get_affiliation(stanza.attr.from) ~= "owner" then + origin.send(st.error_reply(stanza, "auth", "forbidden")); + elseif stanza.attr.type == "get" then + self:send_form(origin, stanza); + elseif stanza.attr.type == "set" then + self:process_form(origin, stanza) + end elseif type == "set" or type == "get" then origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); end -- cgit v1.2.3 From 212ef3eca2cb953331e3d41d07c37b2812e6e0b9 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Wed, 25 Nov 2009 19:46:22 +0100 Subject: SASL: Using locally mapped s_gmatch instead of unavailable gmatch. --- util/sasl/digest-md5.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index 557858b3..f8e0e393 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -102,7 +102,7 @@ local function digest(self, message) local function parse(data) local message = {} -- COMPAT: %z in the pattern to work around jwchat bug (sends "charset=utf-8\0") - for k, v in gmatch(data, [[([%w%-]+)="?([^",%z]*)"?,?]]) do -- FIXME The hacky regex makes me shudder + for k, v in s_gmatch(data, [[([%w%-]+)="?([^",%z]*)"?,?]]) do -- FIXME The hacky regex makes me shudder message[k] = v; end return message; -- cgit v1.2.3 From 1af1640671a3cd5b159f1fcdffca9b1362a16fb6 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Thu, 26 Nov 2009 00:03:16 +0500 Subject: MUC: Added support for the room-destroy owner use case. --- plugins/muc/muc.lib.lua | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/plugins/muc/muc.lib.lua b/plugins/muc/muc.lib.lua index 8fd0a093..002498af 100644 --- a/plugins/muc/muc.lib.lua +++ b/plugins/muc/muc.lib.lua @@ -435,6 +435,26 @@ function room_mt:process_form(origin, stanza) origin.send(st.reply(stanza)); end +function room_mt:destroy(newjid, reason, password) + local pr = st.presence({type = "unavailable"}) + :tag("x", {xmlns = "http://jabber.org/protocol/muc#user"}) + :tag("item", { affiliation='none', role='none' }):up() + :tag("destroy", {jid=newjid}) + if reason then pr:tag("reason"):text(reason):up(); end + if password then pr:tag("password"):text(password):up(); end + for nick, occupant in pairs(self._occupants) do + pr.attr.from = nick; + for jid in pairs(occupant.sessions) do + pr.attr.to = jid; + self:_route_stanza(pr); + self._jid_nick[jid] = nil; + end + self._occupants[nick] = nil; + end + self._data.persistent = nil; + if self.save then self:save(true); end +end + function room_mt:handle_to_room(origin, stanza) -- presence changes and groupchat messages, along with disco/etc local type = stanza.attr.type; local xmlns = stanza.tags[1] and stanza.tags[1].attr.xmlns; @@ -515,7 +535,24 @@ function room_mt:handle_to_room(origin, stanza) -- presence changes and groupcha elseif stanza.attr.type == "get" then self:send_form(origin, stanza); elseif stanza.attr.type == "set" then - self:process_form(origin, stanza) + local child = stanza.tags[1].tags[1]; + if not child then + origin.send(st.error_reply(stanza, "auth", "bad-request")); + elseif child.name == "destroy" then + local newjid = child.attr.jid; + local reason, password; + for _,tag in ipairs(child.tags) do + if tag.name == "reason" then + reason = #tag.tags == 0 and tag[1]; + elseif tag.name == "password" then + password = #tag.tags == 0 and tag[1]; + end + end + self:destroy(newjid, reason, password); + origin.send(st.reply(stanza)); + else + self:process_form(origin, stanza); + end end elseif type == "set" or type == "get" then origin.send(st.error_reply(stanza, "cancel", "service-unavailable")); -- cgit v1.2.3 From 22d8917d8ced0783831e0050e1f0ca4089e273e8 Mon Sep 17 00:00:00 2001 From: Florian Zeitz Date: Wed, 25 Nov 2009 19:58:19 +0000 Subject: util.dataforms: Don't require type when parsing form XML --- util/dataforms.lua | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/util/dataforms.lua b/util/dataforms.lua index 5626172e..a3bde8ca 100644 --- a/util/dataforms.lua +++ b/util/dataforms.lua @@ -93,7 +93,13 @@ function form_t.data(layout, stanza) local data = {}; for field_tag in stanza:childtags() do - local field_type = field_tag.attr.type; + local field_type; + for n, field in ipairs(layout) do + if field.name == field_tag.attr.var then + field_type = field.type; + break; + end + end local reader = field_readers[field_type]; if reader then -- cgit v1.2.3 From 2e3f2e68d7669723ad68d149b2c943ad925cadf8 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Wed, 25 Nov 2009 23:45:45 +0000 Subject: util.serialization: Correctly serialize tables with 'false' as a key, fixes an issue with rosters not saving (thanks mathias, Tobias) --- util/serialization.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/serialization.lua b/util/serialization.lua index 07a099c9..7071d3f7 100644 --- a/util/serialization.lua +++ b/util/serialization.lua @@ -35,7 +35,7 @@ local function _simplesave(o, ind, t, func) elseif type(o) == "string" then func(t, (("%q"):format(o):gsub("\\\n", "\\n"))); elseif type(o) == "table" then - if next(o) then + if next(o) ~= nil then func(t, "{\n"); for k,v in pairs(o) do func(t, indent(ind)); -- cgit v1.2.3 -- cgit v1.2.3 -- cgit v1.2.3 From b544f41cef7692b6d4672b9784637051131d03ad Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 01:09:10 +0000 Subject: Makefile: Fix to install new util/sasl directory --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 93ca8c9b..46dddfdb 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,10 @@ install: prosody.install prosodyctl.install prosody.cfg.lua.install util/encodin install -m755 ./prosodyctl.install $(BIN)/prosodyctl install -m644 core/* $(SOURCE)/core install -m644 net/* $(SOURCE)/net - install -m644 util/* $(SOURCE)/util + install -m644 util/*.lua $(SOURCE)/util + install -m644 util/*.so $(SOURCE)/util + install -d $(SOURCE)/util/sasl + install -m644 util/sasl/* $(SOURCE)/util/sasl install -m644 fallbacks/* $(SOURCE)/fallbacks install -m644 plugins/*.lua $(MODULES) install -d $(MODULES)/muc -- cgit v1.2.3 From a3c705d9eee18ba2a0b6145e1b417386fd9b32d6 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Thu, 26 Nov 2009 12:57:24 +0500 Subject: mod_roster: Ask remote server to remove pending subscription requests when removing roster items. --- plugins/mod_roster.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/plugins/mod_roster.lua b/plugins/mod_roster.lua index 7ca22aa1..52c61a26 100644 --- a/plugins/mod_roster.lua +++ b/plugins/mod_roster.lua @@ -74,18 +74,20 @@ module:add_iq_handler("c2s", "jabber:iq:roster", if not resource and host then if jid ~= from_node.."@"..from_host then if item.attr.subscription == "remove" then - local r_item = session.roster[jid]; + local roster = session.roster; + local r_item = roster[jid]; if r_item then + local to_bare = node and (node.."@"..host) or host; -- bare JID + if r_item.subscription == "both" or r_item.subscription == "from" or (roster.pending and roster.pending[jid]) then + core_post_stanza(session, st.presence({type="unsubscribed", from=session.full_jid, to=to_bare})); + end + if r_item.subscription == "both" or r_item.subscription == "to" or r_item.ask then + core_post_stanza(session, st.presence({type="unsubscribe", from=session.full_jid, to=to_bare})); + end local success, err_type, err_cond, err_msg = rm_remove_from_roster(session, jid); if success then session.send(st.reply(stanza)); rm_roster_push(from_node, from_host, jid); - local to_bare = node and (node.."@"..host) or host; -- bare JID - if r_item.subscription == "both" or r_item.subscription == "from" then - core_post_stanza(session, st.presence({type="unsubscribed", from=session.full_jid, to=to_bare})); - elseif r_item.subscription == "both" or r_item.subscription == "to" then - core_post_stanza(session, st.presence({type="unsubscribe", from=session.full_jid, to=to_bare})); - end else session.send(st.error_reply(stanza, err_type, err_cond, err_msg)); end -- cgit v1.2.3 From cfce018b808e0978ab6d573ef09a4a9d4ec0eddd Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Thu, 26 Nov 2009 13:00:11 +0500 Subject: mod_presence: Removed an unnecessary compatibility workaround which was causing issues with unavailable presence exchange after subscription removal. --- plugins/mod_presence.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugins/mod_presence.lua b/plugins/mod_presence.lua index cda8dab0..939df6b8 100644 --- a/plugins/mod_presence.lua +++ b/plugins/mod_presence.lua @@ -200,9 +200,6 @@ function handle_outbound_presence_subscriptions_and_probes(origin, stanza, from_ rostermanager.roster_push(node, host, to_bare); end core_route_stanza(origin, stanza); - -- COMPAT: Some legacy clients keep displaying unsubscribed contacts as online unless an unavailable presence is sent: - send_presence_of_available_resources(node, host, to_bare, origin, core_route_stanza, - st.presence({ type="unavailable", from=from_bare, to=to_bare, id=stanza.attr.id })); end stanza.attr.from, stanza.attr.to = st_from, st_to; end -- cgit v1.2.3 From 0554c71543ee4a199b056accac6a4bc1bc26d216 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Thu, 26 Nov 2009 13:02:10 +0500 Subject: mod_presence: Use the local host as origin for subscription request acks. --- plugins/mod_presence.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_presence.lua b/plugins/mod_presence.lua index 939df6b8..d3818a5d 100644 --- a/plugins/mod_presence.lua +++ b/plugins/mod_presence.lua @@ -230,7 +230,7 @@ function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_b -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too) end else - core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- acknowledging receipt + core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unavailable"})); -- acknowledging receipt if not rostermanager.is_contact_pending_in(node, host, from_bare) then if rostermanager.set_contact_pending_in(node, host, from_bare) then sessionmanager.send_to_available_resources(node, host, stanza); -- cgit v1.2.3 From de4aa7b47a2c991271943b5c12edfd567cf4de60 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Thu, 26 Nov 2009 15:19:59 +0500 Subject: mod_presence: Use the local host object as the origin for auto-generated subscription approvals and cancellations. --- plugins/mod_presence.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_presence.lua b/plugins/mod_presence.lua index d3818a5d..abbc3a3d 100644 --- a/plugins/mod_presence.lua +++ b/plugins/mod_presence.lua @@ -220,11 +220,11 @@ function handle_inbound_presence_subscriptions_and_probes(origin, stanza, from_b -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too) end else - core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="unsubscribed"})); + core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="unsubscribed"})); end elseif stanza.attr.type == "subscribe" then if rostermanager.is_contact_subscribed(node, host, from_bare) then - core_route_stanza(origin, st.presence({from=to_bare, to=from_bare, type="subscribed"})); -- already subscribed + core_route_stanza(hosts[host], st.presence({from=to_bare, to=from_bare, type="subscribed"})); -- already subscribed -- Sending presence is not clearly stated in the RFC, but it seems appropriate if 0 == send_presence_of_available_resources(node, host, from_bare, origin, core_route_stanza) then -- TODO send last recieved unavailable presence (or we MAY do nothing, which is fine too) -- cgit v1.2.3 From fb4bbe19bf54b792b2321d2a4e95bca9c28360d9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 17:17:54 +0000 Subject: s2smanager: Allow configuration of the dialback_secret in the config --- core/s2smanager.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/s2smanager.lua b/core/s2smanager.lua index 666022d1..f4d46802 100644 --- a/core/s2smanager.lua +++ b/core/s2smanager.lua @@ -36,13 +36,12 @@ local log = logger_init("s2smanager"); local sha256_hash = require "util.hashes".sha256; -local dialback_secret = uuid_gen(); - local adns, dns = require "net.adns", require "net.dns"; local config = require "core.configmanager"; local connect_timeout = config.get("*", "core", "s2s_timeout") or 60; local dns_timeout = config.get("*", "core", "dns_timeout") or 60; local max_dns_depth = config.get("*", "core", "dns_max_depth") or 3; +local dialback_secret = config.get("*", "core", "dialback_secret") or uuid_gen(); incoming_s2s = {}; _G.prosody.incoming_s2s = incoming_s2s; -- cgit v1.2.3 From 4a3a52f7a1e1b36427a6543dab5fe70499021dd9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:08:10 +0000 Subject: net.adns: Some cleanup, happens to also make it compatible with libevent --- net/adns.lua | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/net/adns.lua b/net/adns.lua index c9cb9476..d6beffbb 100644 --- a/net/adns.lua +++ b/net/adns.lua @@ -43,33 +43,32 @@ function cancel(handle, call_handler) end function new_async_socket(sock, resolver) - local newconn, peername = {}, ""; + local peername = ""; local listener = {}; + local handler = {}; function listener.onincoming(conn, data) - dns.feed(sock, data); + dns.feed(handler, data); end function listener.ondisconnect(conn, err) log("warn", "DNS socket for %s disconnected: %s", peername, err); local servers = resolver.server; - if resolver.socketset[newconn.handler] == resolver.best_server and resolver.best_server == #servers then + if resolver.socketset[conn] == resolver.best_server and resolver.best_server == #servers then log("error", "Exhausted all %d configured DNS servers, next lookup will try %s again", #servers, servers[1]); end resolver:servfail(conn); -- Let the magic commence end - newconn.handler, newconn._socket = server.wrapclient(sock, "dns", 53, listener); - if not newconn.handler then + handler = server.wrapclient(sock, "dns", 53, listener); + if not handler then log("warn", "handler is nil"); end - if not newconn._socket then - log("warn", "socket is nil"); - end - newconn.handler.settimeout = function () end - newconn.handler.setsockname = function (_, ...) return sock:setsockname(...); end - newconn.handler.setpeername = function (_, ...) peername = (...); local ret = sock:setpeername(...); _.setsend(sock.send); return ret; end - newconn.handler.connect = function (_, ...) return sock:connect(...) end - newconn.handler.send = function (_, data) _.write(_, data); return _.sendbuffer(); end - return newconn.handler; + + handler.settimeout = function () end + handler.setsockname = function (_, ...) return sock:setsockname(...); end + handler.setpeername = function (_, ...) peername = (...); local ret = sock:setpeername(...); _:set_send(sock.send); return ret; end + handler.connect = function (_, ...) return sock:connect(...) end + handler.send = function (_, data) _:write(data); return _.sendbuffer and _.sendbuffer(); end + return handler; end dns:socket_wrapper_set(new_async_socket); -- cgit v1.2.3 From 9d32568d5d90efa317e4f77a94a678a653547cf0 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:08:47 +0000 Subject: net.server_select: Rename server.setsend() to server.set_send() for consistency --- net/server_select.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_select.lua b/net/server_select.lua index d7970296..3b3f3768 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -427,7 +427,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport pattern = new or pattern return pattern end - handler.setsend = function ( self, newsend ) + handler.set_send = function ( self, newsend ) send = newsend or send return send end -- cgit v1.2.3 From 44b3480e14445945d009e6a7eb2fec94e085920c Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 26 Nov 2009 23:11:02 +0100 Subject: util.sasl: Adding clean_clone() method. --- util/sasl.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/util/sasl.lua b/util/sasl.lua index 7b7db024..e3ae8087 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -90,6 +90,11 @@ function new(realm, profile, forbidden) return s; end +-- get a fresh clone with the same realm, profiles and forbidden mechanisms +function method:clean_clone() + return new(self.realm, self.profile, self:forbidden()) +end + -- set the forbidden mechanisms function method:forbidden( restrict ) if restrict then -- cgit v1.2.3 From df4d4cd44521d393dbe0a3ee8a62fe0add80f2ba Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:15:40 +0000 Subject: net.server_event: Fix to make ontimeout() listener callback work --- net/server_event.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index b467a84d..74dffb94 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -138,7 +138,7 @@ do local callback = function( event ) if EV_TIMEOUT == event then -- timout during connection self.fatalerror = "connection timeout" - self.listener.ontimeout( self ) -- call timeout listener + self:ontimeout() -- call timeout listener self:_close() debug( "new connection failed. id:", self.id, "error:", self.fatalerror ) else @@ -432,6 +432,7 @@ do onconnect = listener.onconnect; -- will be called when client disconnects ondisconnect = listener.ondisconnect; -- will be called when client disconnects onincoming = listener.onincoming; -- will be called when client sends data + ontimeout = listener.ontimeout; -- called when fatal socket timeout occurs eventread = false, eventwrite = false, eventclose = false, eventhandshake = false, eventstarthandshake = false; -- event handler eventconnect = false, eventsession = false; -- more event handler... -- cgit v1.2.3 From c65488fc2befa056ce844e59fa3472c0a297bd7e Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Thu, 26 Nov 2009 23:18:26 +0100 Subject: mod_saslauth: Allow relogins after failed SASL login. --- plugins/mod_saslauth.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 04e33b29..9d4b27a1 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -75,7 +75,7 @@ end local function handle_status(session, status) if status == "failure" then - session.sasl_handler = nil; + session.sasl_handler = sesion.sasl_handler:clean_clone(); elseif status == "success" then local username = nodeprep(session.sasl_handler.username); if not username then -- TODO move this to sessionmanager -- cgit v1.2.3 From 05348c61f37e80e99544ab9f655833a06c88612c Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:18:44 +0000 Subject: net.server_handler: Add stub handlers as fallbacks for those listeners don't implement --- net/server_event.lua | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/net/server_event.lua b/net/server_event.lua index 74dffb94..f5eafaf6 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -405,7 +405,14 @@ do return true end - function interface_mt.onconnect() + -- Stub handlers + function interface_mt:onconnect() + end + function interface_mt:onincoming() + end + function interface_mt:ondisconnect() + end + function interface_mt:ontimeout() end end -- cgit v1.2.3 From e6d079f88724611385a25a1c2554b13bde9bc44a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:19:49 +0000 Subject: net.server_event: Count the number of client connections *upwards* :) --- net/server_event.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index f5eafaf6..8187a7d0 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -263,7 +263,7 @@ do _ = self.eventreadtimeout and self.eventreadtimeout:close( ) _ = self.ondisconnect and self:ondisconnect( self.fatalerror ) -- call ondisconnect listener (wont be the case if handshake failed on connect) _ = self.conn and self.conn:close( ) -- close connection, must also be called outside of any socket registered events! - self._server:counter(-1); + _ = self._server and self._server:counter(-1); self.eventread, self.eventwrite = nil, nil self.eventstarthandshake, self.eventhandshake, self.eventclose = nil, nil, nil self.readcallback, self.writecallback = nil, nil @@ -283,7 +283,7 @@ do function interface_mt:counter(c) if c then - self._connections = self._connections - c + self._connections = self._connections + c end return self._connections end -- cgit v1.2.3 From 944ab8a84e38a7d10257fab36578e37cb571d8cf Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:21:12 +0000 Subject: net.server_event: Hide starttls function when the connection is not SSL-enabled --- net/server_event.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/server_event.lua b/net/server_event.lua index 8187a7d0..79b2f388 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -374,6 +374,12 @@ do function interface_mt:set_sslctx(sslctx) self._sslctx = sslctx; + if sslctx then + self.starttls = nil; -- use starttls() of interface_mt + else + self.starttls = false; -- prevent starttls() + end + end end function interface_mt:starttls() @@ -458,6 +464,9 @@ do _sslctx = sslctx; -- parameters _usingssl = false; -- client is using ssl; } + if not sslctx then + interface.starttls = false -- don't allow TLS + end interface.id = tostring(interface):match("%x+$"); interface.writecallback = function( event ) -- called on write events --vdebug( "new client write event, id/ip/port:", interface, ip, port ) -- cgit v1.2.3 From 7dae521ba5a3fdc39c0bba3e1d5ade1c0812be1e Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:22:03 +0000 Subject: net.server_event: Add set_send() for compatibility with server_select --- net/server_event.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/server_event.lua b/net/server_event.lua index 79b2f388..be006cab 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -380,6 +380,9 @@ do self.starttls = false; -- prevent starttls() end end + + function interface_mt:set_send(new_send) + -- No-op, we always use the underlying connection's send end function interface_mt:starttls() -- cgit v1.2.3 From 422a717900a10b8cd991b339bd0a540840695866 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:23:51 +0000 Subject: net.server_event: tostring() some debug logging parameters --- net/server_event.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index be006cab..5e2424ea 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -538,9 +538,9 @@ do end interface.readcallback = function( event ) -- called on read events - --vdebug( "new client read event, id/ip/port:", interface, ip, port ) + --vdebug( "new client read event, id/ip/port:", tostring(interface.id), tostring(ip), tostring(port) ) if interface.noreading or interface.fatalerror then -- leave this event - --vdebug( "leaving this event because:", interface.noreading or interface.fatalerror ) + --vdebug( "leaving this event because:", tostring(interface.noreading or interface.fatalerror) ) interface.eventread = nil return -1 end @@ -554,7 +554,7 @@ do if interface._usingssl then -- handle luasec if interface.eventwritetimeout then -- ok, in the past writecallback was regged local ret = interface.writecallback( ) -- call it - --vdebug( "tried to write in readcallback, result:", ret ) + --vdebug( "tried to write in readcallback, result:", tostring(ret) ) end if interface.eventreadtimeout then interface.eventreadtimeout:close( ) -- cgit v1.2.3 From 546954e85cee23594c84866db3c56842886ad541 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Thu, 26 Nov 2009 22:25:13 +0000 Subject: net.server_event: Add addclient/wrapclient compatible with server_select, DNS and s2s should now work with libevent \o/ --- net/server_event.lua | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/net/server_event.lua b/net/server_event.lua index 5e2424ea..450bd341 100644 --- a/net/server_event.lua +++ b/net/server_event.lua @@ -693,26 +693,16 @@ local addserver = ( function( ) end end )( ) -local wrapclient = ( function( ) - return function( client, addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) - debug( "try to connect to:", addr, serverport, "with parameters:", pattern, localaddr, localport, sslcfg, startssl ) - local sslctx - if sslcfg then -- handle ssl/new context - if not ssl then - debug "need luasec, but not available" - return nil, "luasec not found" - end - sslctx, err = ssl.newcontext( sslcfg ) - if err then - debug( "cannot create new ssl context:", err ) - return nil, err - end - end +local addclient, wrapclient +do + function wrapclient( client, ip, port, listeners, pattern, sslctx, startssl ) + local interface = handleclient( client, ip, port, nil, pattern, listeners, sslctx ) + interface:_start_session() + return interface + --function handleclient( client, ip, port, server, pattern, listener, _, sslctx ) -- creates an client interface end -end )( ) - -local addclient = ( function( ) - return function( addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) + + function addclient( addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) local client, err = socket.tcp() -- creating new socket if not client then debug( "cannot create socket:", err ) @@ -726,23 +716,35 @@ local addclient = ( function( ) return nil, err end end + local sslctx + if sslcfg then -- handle ssl/new context + if not ssl then + debug "need luasec, but not available" + return nil, "luasec not found" + end + sslctx, err = ssl.newcontext( sslcfg ) + if err then + debug( "cannot create new ssl context:", err ) + return nil, err + end + end local res, err = client:connect( addr, serverport ) -- connect if res or ( err == "timeout" ) then local ip, port = client:getsockname( ) local server = function( ) return nil, "this is a dummy server interface" end - local interface = handleclient( client, ip, port, server, pattern, listener, sslctx ) + local interface = wrapclient( client, ip, serverport, listeners, pattern, sslctx, startssl ) interface:_start_connection( startssl ) - debug( "new connection id:", interface ) + debug( "new connection id:", interface.id ) return interface, err else debug( "new connection failed:", err ) return nil, err end - return wrapclient( client, addr, serverport, listener, pattern, localaddr, localport, sslcfg, startssl ) end -end )( ) +end + local loop = function( ) -- starts the event loop return base:loop( ) -- cgit v1.2.3 From 3a73d58d69b57a0cd40280d35019d5d6410d72b1 Mon Sep 17 00:00:00 2001 From: sjoerd simons Date: Fri, 27 Nov 2009 16:51:05 +0000 Subject: mod_proxy65: Update listener callback names for new server API --- plugins/mod_proxy65.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/mod_proxy65.lua b/plugins/mod_proxy65.lua index f1314a00..4070dc1b 100644 --- a/plugins/mod_proxy65.lua +++ b/plugins/mod_proxy65.lua @@ -31,7 +31,7 @@ local proxy_acl = config_get(host, "core", "proxy65_acl"); local connlistener = { default_port = proxy_port, default_interface = proxy_interface, default_mode = "*a" }; -function connlistener.listener(conn, data) +function connlistener.onincoming(conn, data) local session = sessions[conn] or {}; if session.setup == nil and data ~= nil and data:sub(1):byte() == 0x05 and data:len() > 2 then @@ -94,7 +94,7 @@ function connlistener.listener(conn, data) end end -function connlistener.disconnect(conn, err) +function connlistener.ondisconnect(conn, err) local session = sessions[conn]; if session then if session.sha and transfers[session.sha] then -- cgit v1.2.3 From 243dc405ce0cb16de1d1bed31197492dcc0d1f63 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Nov 2009 17:33:55 +0000 Subject: util.jid: Add join(node, host, resource) function to join the components and return nil if invalid --- util/jid.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/util/jid.lua b/util/jid.lua index ccc8309c..b43247cc 100644 --- a/util/jid.lua +++ b/util/jid.lua @@ -65,4 +65,17 @@ function prep(jid) return host; end +function join(node, host, resource) + if node and host and resource then + return node.."@"..host.."/"..resource; + elseif node and host then + return node.."@"..host; + elseif host and resource then + return host.."/"..resource; + elseif host then + return host; + end + return nil; -- Invalid JID +end + return _M; -- cgit v1.2.3 From 822161982a48e01537d7075226134b5e16a4f5a7 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Nov 2009 17:39:17 +0000 Subject: tests/test.lua: Print the current test being run if verbosity sufficient --- tests/test.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test.lua b/tests/test.lua index f5976a02..2762e42e 100644 --- a/tests/test.lua +++ b/tests/test.lua @@ -149,6 +149,9 @@ function dotest(unitname) print("WARNING: ", unitname.."."..name.." has no test!"); end else + if verbosity >= 4 then + print("INFO: ", "Testing "..unitname.."."..name); + end local line_hook, line_info = new_line_coverage_monitor(fn); debug.sethook(line_hook, "l") local success, ret = pcall(test, f, unit); -- cgit v1.2.3 From 8cfd5950a3a3af7be64ca310a629a147dd6b4017 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Nov 2009 17:41:52 +0000 Subject: tests: Add tests for util.jid.join() --- tests/test_util_jid.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_util_jid.lua b/tests/test_util_jid.lua index fe6ec74e..f579e597 100644 --- a/tests/test_util_jid.lua +++ b/tests/test_util_jid.lua @@ -6,6 +6,16 @@ -- COPYING file in the source package for more information. -- +function join(join) + assert_equal(join("a", "b", "c"), "a@b/c", "builds full JID"); + assert_equal(join("a", "b", nil), "a@b", "builds bare JID"); + assert_equal(join(nil, "b", "c"), "b/c", "builds full host JID"); + assert_equal(join(nil, "b", nil), "b", "builds bare host JID"); + assert_equal(join(nil, nil, nil), nil, "invalid JID is nil"); + assert_equal(join("a", nil, nil), nil, "invalid JID is nil"); + assert_equal(join(nil, nil, "c"), nil, "invalid JID is nil"); + assert_equal(join("a", nil, "c"), nil, "invalid JID is nil"); +end function split(split) @@ -43,3 +53,4 @@ function bare(bare) assert_equal(bare("user@@host/resource"), nil, "invalid JID is nil"); assert_equal(bare("user@host/"), nil, "invalid JID is nil"); end + -- cgit v1.2.3 From 9657f766572002db4702011baf61d0862b061974 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Nov 2009 18:00:47 +0000 Subject: tests/test.lua: Changes to environment handling of tests, and replace module() with dummy function that doesn't alter the current environment --- tests/test.lua | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/tests/test.lua b/tests/test.lua index 2762e42e..b71ccc1f 100644 --- a/tests/test.lua +++ b/tests/test.lua @@ -16,7 +16,7 @@ function run_all_tests() dotest "core.s2smanager" dotest "core.configmanager" dotest "util.stanza" - + dosingletest("test_sasl.lua", "latin1toutf8"); end @@ -106,7 +106,9 @@ function dosingletest(testname, fname) end function dotest(unitname) - local tests = setmetatable({}, { __index = _realG }); + local _fakeG = setmetatable({}, {__index = _realG}); + _fakeG._G = _fakeG; + local tests = setmetatable({}, { __index = _fakeG }); tests.__unit = unitname; local chunk, err = loadfile("test_"..unitname:gsub("%.", "_")..".lua"); if not chunk then @@ -120,19 +122,20 @@ function dotest(unitname) print("WARNING: ", "Failed to initialise tests for "..unitname, err); return; end - if tests.env then setmetatable(tests.env, { __index = _realG }); end - local unit = setmetatable({}, { __index = setmetatable({ _G = tests.env or _G }, { __index = tests.env or _G }) }); - unit._G = unit; _realG._G = unit; + local unit = setmetatable({}, { __index = setmetatable({ _G = tests.env or _fakeG }, { __index = tests.env or _fakeG }) }); local fn = "../"..unitname:gsub("%.", "/")..".lua"; local chunk, err = loadfile(fn); if not chunk then print("WARNING: ", "Failed to load module: "..unitname, err); return; end - + + local oldmodule, old_M = _fakeG.module, _fakeG._M; + _fakeG.module = function () _M = _G end setfenv(chunk, unit); local success, err = pcall(chunk); + _fakeG.module, _fakeG._M = oldmodule, old_M; if not success then print("WARNING: ", "Failed to initialise module: "..unitname, err); return; -- cgit v1.2.3 From 9d337d5a138b6fd7a29d630a48c5415086def06f Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Fri, 27 Nov 2009 18:02:24 +0000 Subject: mod_proxy65: Use new jid.join() from util.jid --- plugins/mod_proxy65.lua | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/plugins/mod_proxy65.lua b/plugins/mod_proxy65.lua index 4070dc1b..504cfc0c 100644 --- a/plugins/mod_proxy65.lua +++ b/plugins/mod_proxy65.lua @@ -14,7 +14,7 @@ if module:get_host_type() ~= "component" then error("proxy65 should be loaded as a component, please see http://prosody.im/doc/components", 0); end -local jid_split = require "util.jid".split; +local jid_split, jid_join = require "util.jid".split, require "util.jid".join; local st = require "util.stanza"; local componentmanager = require "core.componentmanager"; local config_get = require "core.configmanager".get; @@ -137,19 +137,6 @@ local function get_disco_items(stanza) return reply; end -local function _jid_join(node, host, resource) - local ret = host; - if ret then - if node then - ret = node .. "@" .. ret; - end - if resource then - ret = ret .. "/" .. resource; - end - end - return ret; -end - local function get_stream_host(origin, stanza) local reply = replies_cache.stream_host; local err_reply = replies_cache.stream_host_err; @@ -185,7 +172,7 @@ local function get_stream_host(origin, stanza) replies_cache.stream_host = reply; end else - module:log("warn", "Denying use of proxy for %s", tostring(_jid_join(jid_node, jid_host, jid_resource))); + module:log("warn", "Denying use of proxy for %s", tostring(jid_join(jid_node, jid_host, jid_resource))); if err_reply == nil then err_reply = st.iq({type="error", from=host}) :query("http://jabber.org/protocol/bytestreams") -- cgit v1.2.3 From 20937974626ff19fb5951f016ada12760c13ea15 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sat, 28 Nov 2009 11:59:06 +0500 Subject: mod_register: Properly notify contacts of subscription removal on account deletion. --- plugins/mod_register.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/mod_register.lua b/plugins/mod_register.lua index 2ae01fed..bda40124 100644 --- a/plugins/mod_register.lua +++ b/plugins/mod_register.lua @@ -43,21 +43,21 @@ module:add_iq_handler("c2s", "jabber:iq:register", function (session, stanza) session:close({condition = "not-authorized", text = "Account deleted"}); end -- TODO datamanager should be able to delete all user data itself - datamanager.store(username, host, "roster", nil); datamanager.store(username, host, "vcard", nil); datamanager.store(username, host, "private", nil); datamanager.store(username, host, "offline", nil); - --local bare = username.."@"..host; + local bare = username.."@"..host; for jid, item in pairs(roster) do - if jid ~= "pending" then - if item.subscription == "both" or item.subscription == "to" then - -- TODO unsubscribe + if jid and jid ~= "pending" then + if item.subscription == "both" or item.subscription == "from" or (roster.pending and roster.pending[jid]) then + core_post_stanza(hosts[host], st.presence({type="unsubscribed", from=bare, to=jid})); end - if item.subscription == "both" or item.subscription == "from" then - -- TODO unsubscribe + if item.subscription == "both" or item.subscription == "to" or item.ask then + core_post_stanza(hosts[host], st.presence({type="unsubscribe", from=bare, to=jid})); end end end + datamanager.store(username, host, "roster", nil); datamanager.store(username, host, "accounts", nil); -- delete accounts datastore at the end module:log("info", "User removed their account: %s@%s", username, host); module:fire_event("user-deregistered", { username = username, host = host, source = "mod_register", session = session }); -- cgit v1.2.3 From beb6fa2f210bbf8ea8d976f11728d20c5b41ad42 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sat, 28 Nov 2009 12:00:31 +0500 Subject: core.xmlhandlers: Fixed processing of empty namespaces (which caused an issue with jwchat). --- core/xmlhandlers.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/xmlhandlers.lua b/core/xmlhandlers.lua index d679af97..82c2d0b8 100644 --- a/core/xmlhandlers.lua +++ b/core/xmlhandlers.lua @@ -50,7 +50,7 @@ function init_xmlhandlers(session, stream_callbacks) chardata = {}; end local curr_ns,name = tagname:match("^([^\1]*)\1?(.*)$"); - if not name then + if name == "" then curr_ns, name = "", curr_ns; end @@ -63,7 +63,7 @@ function init_xmlhandlers(session, stream_callbacks) local k = attr[i]; attr[i] = nil; local ns, nm = k:match("^([^\1]*)\1?(.*)$"); - if ns and nm then + if nm ~= "" then ns = ns_prefixes[ns]; if ns then attr[ns..":"..nm] = attr[k]; @@ -105,7 +105,7 @@ function init_xmlhandlers(session, stream_callbacks) end function xml_handlers:EndElement(tagname) local curr_ns,name = tagname:match("^([^\1]*)\1?(.*)$"); - if not name then + if name == "" then curr_ns, name = "", curr_ns; end if (not stanza) or (#stanza.last_add > 0 and name ~= stanza.last_add[#stanza.last_add].name) then -- cgit v1.2.3 From fc078849d5ae5736fb9b1ec1e49ad1892cda6f4b Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Nov 2009 14:09:53 +0000 Subject: net.server_select: Update conn.close() to use new connection method convention --- net/server_select.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/server_select.lua b/net/server_select.lua index 3b3f3768..ab2efcb0 100644 --- a/net/server_select.lua +++ b/net/server_select.lua @@ -353,7 +353,7 @@ wrapconnection = function( server, listeners, socket, ip, serverport, clientport handler.shutdown = function( pattern ) return shutdown( socket, pattern ) end - handler.close = function( forced ) + handler.close = function( self, forced ) if not handler then return true; end _readlistlen = removesocket( _readlist, socket, _readlistlen ) _readtimes[ handler ] = nil -- cgit v1.2.3 From f0ffde882a3999a0ce61c321d3f1e449c0f5b8d9 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Nov 2009 15:12:07 +0000 Subject: mod_saslauth: Fix typo in variable name --- plugins/mod_saslauth.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_saslauth.lua b/plugins/mod_saslauth.lua index 9d4b27a1..001f14e2 100644 --- a/plugins/mod_saslauth.lua +++ b/plugins/mod_saslauth.lua @@ -75,7 +75,7 @@ end local function handle_status(session, status) if status == "failure" then - session.sasl_handler = sesion.sasl_handler:clean_clone(); + session.sasl_handler = session.sasl_handler:clean_clone(); elseif status == "success" then local username = nodeprep(session.sasl_handler.username); if not username then -- TODO move this to sessionmanager -- cgit v1.2.3 From 9f1852c7638c1bccecd0120a570ae3f3bfd518af Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Nov 2009 15:12:43 +0000 Subject: util.sasl.plain: Fail gracefully on empty tag --- util/sasl/plain.lua | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 46a86bb9..9ebfa15d 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -21,10 +21,14 @@ module "plain" --SASL PLAIN according to RFC 4616 local function plain(self, message) local response = message - local authorization = s_match(response, "([^%z]+)") - local authentication = s_match(response, "%z([^%z]+)%z") - local password = s_match(response, "%z[^%z]+%z([^%z]+)") - + + local authorization, authentication, password; + if response then + authorization = s_match(response, "([^%z]+)") + authentication = s_match(response, "%z([^%z]+)%z") + password = s_match(response, "%z[^%z]+%z([^%z]+)") + end + if authentication == nil or password == nil then return "failure", "malformed-request"; end @@ -63,4 +67,4 @@ function init(registerMechanism) registerMechanism("PLAIN", {"plain", "plain_test"}, plain); end -return _M; \ No newline at end of file +return _M; -- cgit v1.2.3 From d3bf83037f653b6d07c32482a863dd45c0f24584 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sat, 28 Nov 2009 18:23:25 +0100 Subject: util.sasl: Move some variables to local space. Fix a bug. --- util/sasl.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/util/sasl.lua b/util/sasl.lua index e3ae8087..9c8fff78 100644 --- a/util/sasl.lua +++ b/util/sasl.lua @@ -83,10 +83,11 @@ end -- create a new SASL object which can be used to authenticate clients function new(realm, profile, forbidden) - sasl_i = {profile = profile}; + local sasl_i = {profile = profile}; sasl_i.realm = realm; - s = setmetatable(sasl_i, method); - s:forbidden(sasl_i, forbidden) + local s = setmetatable(sasl_i, method); + if forbidden == nil then forbidden = {} end + s:forbidden(forbidden) return s; end @@ -112,7 +113,7 @@ function method:mechanisms() for backend, f in pairs(self.profile) do if backend_mechanism[backend] then for _, mechanism in ipairs(backend_mechanism[backend]) do - if not sasl_i.restrict:contains(mechanism) then + if not self.restrict:contains(mechanism) then mechanisms[mechanism] = true; end end -- cgit v1.2.3 From b022ba7fc29418504fcbe6890a784d3cef2cb265 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sat, 28 Nov 2009 18:32:41 +0100 Subject: Make some more variables to locals. --- util/sasl/digest-md5.lua | 6 +----- util/sasl/scram.lua | 4 ++-- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index f8e0e393..a14e875b 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -28,10 +28,6 @@ module "digest-md5" --========================= --SASL DIGEST-MD5 according to RFC 2831 -local function digest_response() - - return response, A1, A2 -end local function digest(self, message) --TODO complete support for authzid @@ -174,7 +170,7 @@ local function digest(self, message) local password, state = self.profile.plain(response["username"], self.realm) if state == nil then return "failure", "not-authorized" elseif state == false then return "failure", "account-disabled" end - Y = md5(response["username"]..":"..response["realm"]..":"..password); + local Y = md5(response["username"]..":"..response["realm"]..":"..password); elseif self.profile["digest-md5"] then local Y, state = self.profile["digest-md5"](response["username"], self.realm, response["realm"], response["charset"]) if state == nil then return "failure", "not-authorized" diff --git a/util/sasl/scram.lua b/util/sasl/scram.lua index 4413e2a6..1e9c6f7d 100644 --- a/util/sasl/scram.lua +++ b/util/sasl/scram.lua @@ -54,7 +54,7 @@ local function Hi(hmac, str, salt, i) local Ust = hmac(str, salt.."\0\0\0\1"); local res = Ust; for n=1,i-1 do - Und = hmac(str, Ust) + local Und = hmac(str, Ust) res = binaryXOR(res, Und) Ust = Und end @@ -118,7 +118,7 @@ local function scram_sha_1(self, message) local password; if self.profile.plain then - password, state = self.profile.plain(self.state.name, self.realm) + local password, state = self.profile.plain(self.state.name, self.realm) if state == nil then return "failure", "not-authorized" elseif state == false then return "failure", "account-disabled" end password = saslprep(password); -- cgit v1.2.3 From f0b237f8bd70aa6645a282534caa491d3d59cd85 Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Nov 2009 17:39:05 +0000 Subject: mod_console: Update for new server API, fixes traceback when closing console sessions --- plugins/mod_console.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/mod_console.lua b/plugins/mod_console.lua index 209981ce..9962b3f6 100644 --- a/plugins/mod_console.lua +++ b/plugins/mod_console.lua @@ -37,7 +37,7 @@ function console:new_session(conn) local session = { conn = conn; send = function (t) w(tostring(t)); end; print = function (t) w("| "..tostring(t).."\n"); end; - disconnect = function () conn.close(); end; + disconnect = function () conn:close(); end; }; session.env = setmetatable({}, default_env_mt); -- cgit v1.2.3 From 4267a24ab79792f5e8ea993f84a6902beaabe94a Mon Sep 17 00:00:00 2001 From: Matthew Wild Date: Sat, 28 Nov 2009 17:40:37 +0000 Subject: xmppserver_listener: Update for new server API, fixes traceback when closing s2s connections --- net/xmppserver_listener.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/net/xmppserver_listener.lua b/net/xmppserver_listener.lua index 3c038b83..53a9d354 100644 --- a/net/xmppserver_listener.lua +++ b/net/xmppserver_listener.lua @@ -100,10 +100,10 @@ local function session_close(session, reason) end end session.sends2s(""); - if session.notopen or not session.conn.close() then - session.conn.close(true); -- Force FIXME: timer? + if session.notopen or not session.conn:close() then + session.conn:close(true); -- Force FIXME: timer? end - session.conn.close(); + session.conn:close(); xmppserver.ondisconnect(session.conn, "stream error"); end end -- cgit v1.2.3 From 513d6dfa4aaf3a68d89c4ecb150c3a89f25f8539 Mon Sep 17 00:00:00 2001 From: Tobias Markmann Date: Sat, 28 Nov 2009 18:58:58 +0100 Subject: util.sasl.digest-md5: Fixing some variable access. --- util/sasl/digest-md5.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/util/sasl/digest-md5.lua b/util/sasl/digest-md5.lua index a14e875b..c4507d11 100644 --- a/util/sasl/digest-md5.lua +++ b/util/sasl/digest-md5.lua @@ -166,13 +166,14 @@ local function digest(self, message) --TODO maybe realm support self.username = response["username"]; + local Y, state; if self.profile.plain then local password, state = self.profile.plain(response["username"], self.realm) if state == nil then return "failure", "not-authorized" elseif state == false then return "failure", "account-disabled" end - local Y = md5(response["username"]..":"..response["realm"]..":"..password); + Y = md5(response["username"]..":"..response["realm"]..":"..password); elseif self.profile["digest-md5"] then - local Y, state = self.profile["digest-md5"](response["username"], self.realm, response["realm"], response["charset"]) + Y, state = self.profile["digest-md5"](response["username"], self.realm, response["realm"], response["charset"]) if state == nil then return "failure", "not-authorized" elseif state == false then return "failure", "account-disabled" end elseif self.profile["digest-md5-test"] then -- cgit v1.2.3 From 8fcbba4b0fccb299121083d97cab065e51fba9d1 Mon Sep 17 00:00:00 2001 From: Waqas Hussain Date: Sun, 29 Nov 2009 18:30:33 +0500 Subject: util.sasl.plain: A little refactoring. --- util/sasl/plain.lua | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/util/sasl/plain.lua b/util/sasl/plain.lua index 9ebfa15d..a4c8765d 100644 --- a/util/sasl/plain.lua +++ b/util/sasl/plain.lua @@ -17,26 +17,23 @@ local log = require "util.logger".init("sasl"); module "plain" ---========================= ---SASL PLAIN according to RFC 4616 +-- ================================ +-- SASL PLAIN according to RFC 4616 local function plain(self, message) - local response = message - - local authorization, authentication, password; - if response then - authorization = s_match(response, "([^%z]+)") - authentication = s_match(response, "%z([^%z]+)%z") - password = s_match(response, "%z[^%z]+%z([^%z]+)") + if not message then + return "failure", "malformed-request"; end - - if authentication == nil or password == nil then + + local authorization, authentication, password = s_match(message, "^([^%z]+)%z([^%z]+)%z([^%z]+)"); + + if not authorization then return "failure", "malformed-request"; end - + -- SASLprep password and authentication authentication = saslprep(authentication); password = saslprep(password); - + if (not password) or (password == "") or (not authentication) or (authentication == "") then log("debug", "Username or password violates SASLprep."); return "failure", "malformed-request", "Invalid username or password."; -- cgit v1.2.3