aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/sessionmanager.lua2
-rw-r--r--net/http/server.lua29
-rw-r--r--plugins/mod_http.lua8
-rw-r--r--plugins/mod_s2s/mod_s2s.lua6
-rw-r--r--tests/test.lua1
-rw-r--r--tests/test_utf8.lua19
-rw-r--r--tests/utf8_sequences.txt52
-rw-r--r--util-src/encodings.c117
8 files changed, 207 insertions, 27 deletions
diff --git a/core/sessionmanager.lua b/core/sessionmanager.lua
index 476de931..8767e869 100644
--- a/core/sessionmanager.lua
+++ b/core/sessionmanager.lua
@@ -114,7 +114,7 @@ end
-- returns nil, err_type, err, err_message on failure
function bind_resource(session, resource)
if not session.username then return nil, "auth", "not-authorized", "Cannot bind resource before authentication"; end
- if session.resource then return nil, "cancel", "already-bound", "Cannot bind multiple resources on a single connection"; end
+ if session.resource then return nil, "cancel", "not-allowed", "Cannot bind multiple resources on a single connection"; end
-- We don't support binding multiple resources
local event_payload = { session = session, resource = resource };
diff --git a/net/http/server.lua b/net/http/server.lua
index 581630d0..f091595c 100644
--- a/net/http/server.lua
+++ b/net/http/server.lua
@@ -189,7 +189,6 @@ function handle_request(conn, request, finish_cb)
persistent = persistent;
conn = conn;
send = _M.send_response;
- done = _M.finish_response;
finish_cb = finish_cb;
};
conn._http_open_response = response;
@@ -209,7 +208,7 @@ function handle_request(conn, request, finish_cb)
err_code, err = 400, "Missing or invalid 'Host' header";
end
end
-
+
if err then
response.status_code = err_code;
response:send(events.fire_event("http-error", { code = err_code, message = err }));
@@ -218,7 +217,7 @@ function handle_request(conn, request, finish_cb)
local event = request.method.." "..host..request.path:match("[^?]*");
local payload = { request = request, response = response };
- log("debug", event);
+ log("debug", "Firing event: %s", event);
local result = events.fire_event(event, payload);
if result ~= nil then
if result ~= true then
@@ -251,30 +250,24 @@ function handle_request(conn, request, finish_cb)
response.status_code = 404;
response:send(events.fire_event("http-error", { code = 404 }));
end
-local function prepare_header(response)
+function _M.send_response(response, body)
+ if response.finished then return; end
+ response.finished = true;
+ response.conn._http_open_response = nil;
+
local status_line = "HTTP/"..response.request.httpversion.." "..(response.status or codes[response.status_code]);
local headers = response.headers;
+ body = body or response.body or "";
+ headers.content_length = #body;
+
local output = { status_line };
for k,v in pairs(headers) do
t_insert(output, headerfix[k]..v);
end
t_insert(output, "\r\n\r\n");
- return output;
-end
-_M.prepare_header = prepare_header;
-function _M.send_response(response, body)
- if response.finished then return; end
- body = body or response.body or "";
- response.headers.content_length = #body;
- local output = prepare_header(response);
t_insert(output, body);
+
response.conn:write(t_concat(output));
- response:done();
-end
-function _M.finish_response(response)
- if response.finished then return; end
- response.finished = true;
- response.conn._http_open_response = nil;
if response.on_destroy then
response:on_destroy();
response.on_destroy = nil;
diff --git a/plugins/mod_http.lua b/plugins/mod_http.lua
index 8bda1cac..086887fb 100644
--- a/plugins/mod_http.lua
+++ b/plugins/mod_http.lua
@@ -74,6 +74,8 @@ function moduleapi.http_url(module, app_name, default_path)
return url_build(url);
end
end
+ module:log("warn", "No http ports enabled, can't generate an external URL");
+ return "http://disabled.invalid/";
end
function module.add_host(module)
@@ -118,6 +120,12 @@ function module.add_host(module)
module:log("error", "Invalid route in %s, %q. See http://prosody.im/doc/developers/http#routes", app_name, key);
end
end
+ local services = portmanager.get_active_services();
+ if services:get("https") or services:get("http") then
+ module:log("debug", "Serving '%s' at %s", app_name, module:http_url(app_name, app_path));
+ else
+ module:log("warn", "Not listening on any ports, '%s' will be unreachable", app_name);
+ end
end
local function http_app_removed(event)
diff --git a/plugins/mod_s2s/mod_s2s.lua b/plugins/mod_s2s/mod_s2s.lua
index f9165f20..a58c6421 100644
--- a/plugins/mod_s2s/mod_s2s.lua
+++ b/plugins/mod_s2s/mod_s2s.lua
@@ -499,6 +499,12 @@ function session_stream_attrs(session, from, to, attr)
if not from or (hosts[from] and hosts[from].modules.dialback) then
attr["xmlns:db"] = 'jabber:server:dialback';
end
+ if not from then
+ attr.from = '';
+ end
+ if not to then
+ attr.to = '';
+ end
end
-- Session initialization logic shared by incoming and outgoing
diff --git a/tests/test.lua b/tests/test.lua
index f7475a80..78f2b234 100644
--- a/tests/test.lua
+++ b/tests/test.lua
@@ -22,6 +22,7 @@ function run_all_tests()
dotest "util.sasl.scram"
dosingletest("test_sasl.lua", "latin1toutf8");
+ dosingletest("test_utf8.lua", "valid");
end
local verbosity = tonumber(arg[1]) or 2;
diff --git a/tests/test_utf8.lua b/tests/test_utf8.lua
new file mode 100644
index 00000000..481eff5d
--- /dev/null
+++ b/tests/test_utf8.lua
@@ -0,0 +1,19 @@
+package.cpath = "../?.so"
+package.path = "../?.lua";
+
+function valid()
+ local encodings = require "util.encodings";
+ local utf8 = assert(encodings.utf8, "no encodings.utf8 module");
+
+ for line in io.lines("utf8_sequences.txt") do
+ local data = line:match(":%s*([^#]+)"):gsub("%s+", ""):gsub("..", function (c) return string.char(tonumber(c, 16)); end)
+ local expect = line:match("(%S+):");
+ if expect ~= "pass" and expect ~= "fail" then
+ error("unknown expectation: "..line:match("^[^:]+"));
+ end
+ local prefix, style = " ", valid_style;
+ local valid = utf8.valid(data);
+ assert_equal(valid, utf8.valid(data.." "));
+ assert_equal(valid, expect == "pass", line);
+ end
+end
diff --git a/tests/utf8_sequences.txt b/tests/utf8_sequences.txt
new file mode 100644
index 00000000..1b967b2e
--- /dev/null
+++ b/tests/utf8_sequences.txt
@@ -0,0 +1,52 @@
+Should pass: 41 42 43 # Simple ASCII - abc
+Should pass: 41 42 c3 87 # "ABÇ"
+Should pass: 41 42 e1 b8 88 # "ABḈ"
+Should pass: 41 42 f0 9d 9c 8d # "AB𝜍"
+Should pass: F4 8F BF BF # Last valid sequence (U+10FFFF)
+Should fail: F4 90 80 80 # First invalid sequence (U+110000)
+Should fail: 80 81 82 83 # Invalid sequence (invalid start byte)
+Should fail: C2 C3 # Invalid sequence (invalid continuation byte)
+Should fail: C0 43 # Overlong sequence
+Should fail: F5 80 80 80 # U+140000 (out of range)
+Should fail: ED A0 80 # U+D800 (forbidden by RFC 3629)
+Should fail: ED BF BF # U+DFFF (forbidden by RFC 3629)
+Should pass: ED 9F BF # U+D7FF (U+D800 minus 1: allowed)
+Should pass: EE 80 80 # U+E000 (U+D7FF plus 1: allowed)
+Should fail: C0 # Invalid start byte
+Should fail: C1 # Invalid start byte
+Should fail: C2 # Incomplete sequence
+Should fail: F8 88 80 80 80 # 6-byte sequence
+Should pass: 7F # Last valid 1-byte sequence (U+00007F)
+Should pass: DF BF # Last valid 2-byte sequence (U+0007FF)
+Should pass: EF BF BF # Last valid 3-byte sequence (U+00FFFF)
+Should pass: 00 # First valid 1-byte sequence (U+000000)
+Should pass: C2 80 # First valid 2-byte sequence (U+000080)
+Should pass: E0 A0 80 # First valid 3-byte sequence (U+000800)
+Should pass: F0 90 80 80 # First valid 4-byte sequence (U+000800)
+Should fail: F8 88 80 80 80 # First 5-byte sequence - invalid per RFC 3629
+Should fail: FC 84 80 80 80 80 # First 6-byte sequence - invalid per RFC 3629
+Should pass: EF BF BD # U+00FFFD (replacement character)
+Should fail: 80 # First continuation byte
+Should fail: BF # Last continuation byte
+Should fail: 80 BF # 2 continuation bytes
+Should fail: 80 BF 80 # 3 continuation bytes
+Should fail: 80 BF 80 BF # 4 continuation bytes
+Should fail: 80 BF 80 BF 80 # 5 continuation bytes
+Should fail: 80 BF 80 BF 80 BF # 6 continuation bytes
+Should fail: 80 BF 80 BF 80 BF 80 # 7 continuation bytes
+Should fail: FE # Impossible byte
+Should fail: FF # Impossible byte
+Should fail: FE FE FF FF # Impossible bytes
+Should fail: C0 AF # Overlong "/"
+Should fail: E0 80 AF # Overlong "/"
+Should fail: F0 80 80 AF # Overlong "/"
+Should fail: F8 80 80 80 AF # Overlong "/"
+Should fail: FC 80 80 80 80 AF # Overlong "/"
+Should fail: C0 80 AF # Overlong "/" (invalid)
+Should fail: C1 BF # Overlong
+Should fail: E0 9F BF # Overlong
+Should fail: F0 8F BF BF # Overlong
+Should fail: F8 87 BF BF BF # Overlong
+Should fail: FC 83 BF BF BF BF # Overlong
+Should pass: EF BF BE # U+FFFE (invalid unicode, valid UTF-8)
+Should pass: EF BF BF # U+FFFF (invalid unicode, valid UTF-8)
diff --git a/util-src/encodings.c b/util-src/encodings.c
index 2d5d49d4..0841e29f 100644
--- a/util-src/encodings.c
+++ b/util-src/encodings.c
@@ -1,6 +1,7 @@
/* Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
+-- Copyright (C) 1994-2015 Lua.org, PUC-Rio.
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
@@ -120,6 +121,88 @@ static const luaL_Reg Reg_base64[] =
{ NULL, NULL }
};
+/******************* UTF-8 ********************/
+
+/*
+ * Adapted from Lua 5.3
+ * Needed because libidn does not validate that input is valid UTF-8
+ */
+
+#define MAXUNICODE 0x10FFFF
+
+/*
+ * Decode one UTF-8 sequence, returning NULL if byte sequence is invalid.
+ */
+static const char *utf8_decode (const char *o, int *val) {
+ static unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF};
+ const unsigned char *s = (const unsigned char *)o;
+ unsigned int c = s[0];
+ unsigned int res = 0; /* final result */
+ if (c < 0x80) /* ascii? */
+ res = c;
+ else {
+ int count = 0; /* to count number of continuation bytes */
+ while (c & 0x40) { /* still have continuation bytes? */
+ int cc = s[++count]; /* read next byte */
+ if ((cc & 0xC0) != 0x80) /* not a continuation byte? */
+ return NULL; /* invalid byte sequence */
+ res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */
+ c <<= 1; /* to test next bit */
+ }
+ res |= ((c & 0x7F) << (count * 5)); /* add first byte */
+ if (count > 3 || res > MAXUNICODE || res <= limits[count] || (0xd800 <= res && res <= 0xdfff) )
+ return NULL; /* invalid byte sequence */
+ s += count; /* skip continuation bytes read */
+ }
+ if (val) *val = res;
+ return (const char *)s + 1; /* +1 to include first byte */
+}
+
+/*
+ * Check that a string is valid UTF-8
+ * Returns NULL if not
+ */
+const char* check_utf8 (lua_State *L, int idx, size_t *l) {
+ size_t pos, len;
+ const char *s = luaL_checklstring(L, 1, &len);
+ pos = 0;
+ while (pos <= len) {
+ const char *s1 = utf8_decode(s + pos, NULL);
+ if (s1 == NULL) { /* conversion error? */
+ return NULL;
+ }
+ pos = s1 - s;
+ }
+ if(l != NULL) {
+ *l = len;
+ }
+ return s;
+}
+
+static int Lutf8_valid(lua_State *L) {
+ lua_pushboolean(L, check_utf8(L, 1, NULL) != NULL);
+ return 1;
+}
+
+static int Lutf8_length(lua_State *L) {
+ size_t len;
+ if(!check_utf8(L, 1, &len)) {
+ lua_pushnil(L);
+ lua_pushliteral(L, "invalid utf8");
+ return 2;
+ }
+ lua_pushinteger(L, len);
+ return 1;
+}
+
+static const luaL_Reg Reg_utf8[] =
+{
+ { "valid", Lutf8_valid },
+ { "length", Lutf8_length },
+ { NULL, NULL }
+};
+
+
/***************** STRINGPREP *****************/
#ifdef USE_STRINGPREP_ICU
@@ -216,8 +299,8 @@ static int stringprep_prep(lua_State *L, const Stringprep_profile *profile)
lua_pushnil(L);
return 1;
}
- s = lua_tolstring(L, 1, &len);
- if (len >= 1024) {
+ s = check_utf8(L, 1, &len);
+ if (s == NULL || len >= 1024 || len != strlen(s)) {
lua_pushnil(L);
return 1; /* TODO return error message */
}
@@ -324,7 +407,11 @@ static int Lidna_to_unicode(lua_State *L) /** idna.to_unicode(s) */
static int Lidna_to_ascii(lua_State *L) /** idna.to_ascii(s) */
{
size_t len;
- const char *s = luaL_checklstring(L, 1, &len);
+ const char *s = check_utf8(L, 1, &len);
+ if (s == NULL || len != strlen(s)) {
+ lua_pushnil(L);
+ return 1; /* TODO return error message */
+ }
char* output = NULL;
int ret = idna_to_ascii_8z(s, &output, IDNA_USE_STD3_ASCII_RULES);
if (ret == IDNA_SUCCESS) {
@@ -365,26 +452,40 @@ static const luaL_Reg Reg_idna[] =
/***************** end *****************/
+static const luaL_Reg Reg[] =
+{
+ { NULL, NULL }
+};
+
LUALIB_API int luaopen_util_encodings(lua_State *L)
{
#ifdef USE_STRINGPREP_ICU
init_icu();
#endif
- lua_newtable(L);
+ luaL_register(L, "encodings", Reg);
+ lua_pushliteral(L, "base64");
lua_newtable(L);
luaL_register(L, NULL, Reg_base64);
- lua_setfield(L, -2, "base64");
+ lua_settable(L,-3);
+ lua_pushliteral(L, "stringprep");
lua_newtable(L);
luaL_register(L, NULL, Reg_stringprep);
- lua_setfield(L, -2, "stringprep");
+ lua_settable(L,-3);
+ lua_pushliteral(L, "idna");
lua_newtable(L);
luaL_register(L, NULL, Reg_idna);
- lua_setfield(L, -2, "idna");
+ lua_settable(L,-3);
+
+ lua_pushliteral(L, "utf8");
+ lua_newtable(L);
+ luaL_register(L, NULL, Reg_utf8);
+ lua_settable(L, -3);
+ lua_pushliteral(L, "version"); /** version */
lua_pushliteral(L, "-3.14");
- lua_setfield(L, -2, "version");
+ lua_settable(L,-3);
return 1;
}