From 27f7ce444f632f98d4baf6531e435f3b3b5e5560 Mon Sep 17 00:00:00 2001
From: Matthias Schiffer <mschiffer@universe-factory.net>
Date: Mon, 26 Feb 2018 03:25:01 +0100
Subject: [PATCH] gluon-status-page: when visiting via a next-node address,
 redirect to a unique address

A downside of this behaviour is that the page does not work for IPv4-only
clients, as the redirect will always point at an IPv6 address.

Still, it seems like a good idea to enforce the redirect even from the IPv4
next-node address, as switching nodes while being connected to the status
page would lead to unexpected behaviour.
---
 .../gluon/status-page/view/status-page.html   |  3 -
 .../status-page/controller/status-page.lua    | 86 ++++++++++++++++++-
 2 files changed, 85 insertions(+), 4 deletions(-)

diff --git a/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html b/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html
index 2d023af76..a37666326 100644
--- a/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html
+++ b/package/gluon-status-page/files/lib/gluon/status-page/view/status-page.html
@@ -1,6 +1,5 @@
 <%-
 	local fs = require 'nixio.fs'
-	local json = require 'jsonc'
 	local ubus = require 'ubus'
 	local util = require 'gluon.util'
 
@@ -44,8 +43,6 @@
 
 	local interfaces = get_interfaces()
 
-	local nodeinfo = json.parse(util.exec('exec gluon-neighbour-info -d ::1 -p 1001 -t 1 -c 1 -r nodeinfo'))
-
 	local function sorted(t)
 		t = {unpack(t)}
 		table.sort(t)
diff --git a/package/gluon-status-page/luasrc/lib/gluon/status-page/controller/status-page.lua b/package/gluon-status-page/luasrc/lib/gluon/status-page/controller/status-page.lua
index 18ea1864c..a997ff5f4 100644
--- a/package/gluon-status-page/luasrc/lib/gluon/status-page/controller/status-page.lua
+++ b/package/gluon-status-page/luasrc/lib/gluon/status-page/controller/status-page.lua
@@ -1,3 +1,87 @@
+local json = require 'jsonc'
+local site = require 'gluon.site'
+local util = require 'gluon.util'
+
+local function parse_ip(addr)
+	if not addr then return end
+
+	local ip4 = {addr:match('(%d+)%.(%d+)%.(%d+)%.(%d+)')}
+	if ip4[1] then
+		local ret = {}
+
+		for i, part in ipairs(ip4) do
+			ret[i] = tonumber(part)
+		end
+		return ret
+	end
+
+	if not addr:match('^[:%x]+$') then
+		return
+	end
+
+	if addr:sub(0, 2) == '::' then
+		addr = '0' .. addr
+	end
+	if addr:sub(-2) == '::' then
+		addr = addr .. '0'
+	end
+
+	addr = addr .. ':'
+
+	local groups, groups1 = {}, {}
+	for part in addr:gmatch('([^:]*):') do
+		if part == '' then
+			groups1 = groups
+			groups = {}
+		else
+			groups[#groups+1] = tonumber(part, 16)
+		end
+	end
+
+	while #groups + #groups1 < 8 do
+		groups1[#groups1+1] = 0
+	end
+	for _, group in ipairs(groups) do
+		groups1[#groups1+1] = group
+	end
+
+	return groups1
+end
+
+local function match(a, b, n)
+	if not a or not b then return false end
+
+	for i = 1, n do
+		if a[i] ~= b[i] then
+			return false
+		end
+	end
+
+	return true
+end
+
 entry({}, call(function(http, renderer)
-	renderer.render('status-page', nil, 'gluon-status-page')
+	local nodeinfo = json.parse(util.exec('exec gluon-neighbour-info -d ::1 -p 1001 -t 1 -c 1 -r nodeinfo'))
+
+	local node_ip = parse_ip(http:getenv('SERVER_ADDR'))
+	if node_ip and (
+			match(node_ip, parse_ip(site.next_node.ip4()), 8) or
+			match(node_ip, parse_ip(site.next_node.ip6()), 8)
+	) then
+		-- The user has visited the status page via a next-node address
+		-- Redirect the user the a unique address to avoid switching
+		-- nodes
+		local prefix = parse_ip(site.prefix6():match('^[^/]+'))
+		for _, addr in ipairs(nodeinfo.network.addresses) do
+			if match(prefix, parse_ip(addr), 4) then
+				http:header('Cache-Control', 'no-cache, no-store, must-revalidate')
+				http:redirect('http://[' .. addr .. ']' .. http:getenv('REQUEST_URI'))
+				http:close()
+				return
+			end
+		end
+	end
+
+
+	renderer.render('status-page', { nodeinfo = nodeinfo }, 'gluon-status-page')
 end))
-- 
GitLab