rttp initial version -- insecure

This commit is contained in:
kepler155c@gmail.com 2018-03-30 13:12:46 -04:00
parent a17677730f
commit 3dd351cc86
8 changed files with 272 additions and 4 deletions

57
sys/apis/fs/redfs.lua Normal file
View File

@ -0,0 +1,57 @@
--[[
Mount a readonly file system from another computer across rednet. The
target computer must be running OpusOS or redserver.
Syntax:
rn://<id>/directory/subdir
Examples:
rn://12/usr/etc
rn://8/usr
]]--
local rttp = require('rttp')
local fs = _G.fs
local redfs = { }
local function getListing(uri)
local success, response = rttp.get(uri .. '?recursive=true')
if not success then
error(response)
end
if response.statusCode ~= 200 then
error('Received response ' .. response.statusCode)
end
local list = { }
for _,v in pairs(response.data) do
if not v.isDir then
list[v.path] = {
url = uri .. '/' .. v.path,
size = v.size,
}
end
end
return list
end
function redfs.mount(dir, uri)
if not uri then
error('redfs syntax: uri')
end
local list = getListing(uri)
for path, entry in pairs(list) do
if not fs.exists(fs.combine(dir, path)) then
local node = fs.mount(fs.combine(dir, path), 'urlfs', entry.url)
node.size = entry.size
end
end
end
return redfs

View File

@ -1,3 +1,4 @@
local rttp = require('rttp')
local Util = require('util')
local fs = _G.fs
@ -50,7 +51,12 @@ function urlfs.open(node, fn, fl)
local c = node.cache
if not c then
c = Util.httpGet(node.url)
if node.url:match('^([%w][%w%+%-%.]*)%:') == 'rn' then
local s, response = rttp.get(node.url)
c = s and response.statusCode == 200 and response.data
else
c = Util.httpGet(node.url)
end
if c then
node.cache = c
node.size = #c

95
sys/apis/rttp.lua Normal file
View File

@ -0,0 +1,95 @@
local device = _G.device
local os = _G.os
local rttp = { }
local computerId = os.getComputerID()
local function parse(url, default)
-- initialize default parameters
local parsed = {}
local authority
for i,v in pairs(default or parsed) do parsed[i] = v end
-- remove whitespace
-- url = string.gsub(url, "%s", "")
-- Decode unreserved characters
url = string.gsub(url, "%%(%x%x)", function(hex)
local char = string.char(tonumber(hex, 16))
if string.match(char, "[a-zA-Z0-9._~-]") then
return char
end
-- Hex encodings that are not unreserved must be preserved.
return nil
end)
-- get fragment
url = string.gsub(url, "#(.*)$", function(f)
parsed.fragment = f
return ""
end)
-- get scheme. Lower-case according to RFC 3986 section 3.1.
url = string.gsub(url, "^(%w[%w.+-]*):",
function(s) parsed.scheme = string.lower(s); return "" end)
-- get authority
url = string.gsub(url, "^//([^/]*)", function(n)
authority = n
return ""
end)
-- get query stringing
url = string.gsub(url, "%?(.*)", function(q)
parsed.query = q
return ""
end)
-- get params
url = string.gsub(url, "%;(.*)", function(p)
parsed.params = p
return ""
end)
-- path is whatever was left
parsed.path = url
-- Represents host:port, port = nil if not used.
if authority then
authority = string.gsub(authority, ":(%d+)$",
function(p) parsed.port = tonumber(p); return "" end)
if authority ~= "" then
parsed.host = authority
end
end
return parsed
end
function rttp.get(url)
local modem = device.wireless_modem or error('Modem not found')
local parsed = parse(url, { port = 80 })
parsed.host = tonumber(parsed.host) or error('Invalid url')
for i = 16384, 32767 do
if not modem.isOpen(i) then
modem.open(i)
local path = parsed.query and parsed.path .. '?' .. parsed.query or parsed.path
modem.transmit(parsed.port, parsed.host, {
method = 'GET',
replyAddress = computerId,
replyPort = i,
path = path,
})
local timerId = os.startTimer(3)
repeat
local event, id, dport, dhost, response = os.pullEvent()
if event == 'modem_message' and
dport == i and
dhost == computerId and
type(response) == 'table' then
modem.close(i)
return true, response
end
until event == 'timer' and id == timerId
return false, 'timeout'
end
end
end
return rttp

View File

@ -2,7 +2,7 @@
local fs = _G.fs
local http = _G.http
_G.OPUS_BRANCH = 'master-1.8'
_G.OPUS_BRANCH = 'develop-1.8'
local GIT_REPO = 'kepler155c/opus/' .. _G.OPUS_BRANCH
local BASE = 'https://raw.githubusercontent.com/' .. GIT_REPO

View File

@ -149,6 +149,7 @@ function fs.complete(partial, dir, includeFiles, includeSlash)
end
function fs.listEx(dir)
dir = fs.combine(dir, '')
local node = getNode(dir)
if node.fs.listEx then
return node.fs.listEx(node, dir)

View File

@ -252,7 +252,7 @@ local function init(...)
local files = fs.list(dir)
table.sort(files)
for _,file in ipairs(files) do
local level = file:match('(%d).%S+.lua')
local level = file:match('(%d).%S+.lua') or 99
if tonumber(level) <= runLevel then
local s, m = shell.run(fs.combine(dir, file))
if not s then

109
sys/network/redserver.lua Normal file
View File

@ -0,0 +1,109 @@
local Event = require('event')
local Util = require('util')
local fs = _G.fs
local modem = _G.device.wireless_modem
local os = _G.os
local computerId = os.getComputerID()
modem.open(80)
-- https://github.com/golgote/neturl/blob/master/lib/net/url.lua
local function parseQuery(str)
local sep = '&'
local values = {}
for key,val in str:gmatch(string.format('([^%q=]+)(=*[^%q=]*)', sep, sep)) do
--local key = decode(key)
local keys = {}
key = key:gsub('%[([^%]]*)%]', function(v)
-- extract keys between balanced brackets
if string.find(v, "^-?%d+$") then
v = tonumber(v)
--else
--v = decode(v)
end
table.insert(keys, v)
return "="
end)
key = key:gsub('=+.*$', "")
key = key:gsub('%s', "_") -- remove spaces in parameter name
val = val:gsub('^=+', "")
if not values[key] then
values[key] = {}
end
if #keys > 0 and type(values[key]) ~= 'table' then
values[key] = {}
elseif #keys == 0 and type(values[key]) == 'table' then
values[key] = val --decode(val)
end
local t = values[key]
for i,k in ipairs(keys) do
if type(t) ~= 'table' then
t = {}
end
if k == "" then
k = #t+1
end
if not t[k] then
t[k] = {}
end
if i == #keys then
t[k] = val --decode(val)
end
t = t[k]
end
end
return values
end
local function getListing(path, recursive)
local list = { }
local function listing(p)
for _, f in pairs(fs.listEx(p)) do
local abs = fs.combine(p, f.name)
table.insert(list, {
isDir = f.isDir,
path = string.sub(abs, #path + 1),
size = f.size,
})
if recursive and f.isDir then
listing(abs)
end
end
end
listing(path)
return list
end
Event.on('modem_message', function(_, _, dport, dhost, request)
if dport == 80 and dhost == computerId and type(request) == 'table' then
if request.method == 'GET' then
local query
local path = request.path:gsub('%?(.*)', function(v)
query = parseQuery(v)
return ''
end)
if fs.isDir(path) then
modem.transmit(request.replyPort, request.replyAddress, {
statusCode = 200,
contentType = 'table/directory',
data = getListing(path, query and query.recursive == 'true'),
})
elseif fs.exists(path) then
modem.transmit(request.replyPort, request.replyAddress, {
statusCode = 200,
contentType = 'table/file',
data = Util.readFile(path),
})
else
modem.transmit(request.replyPort, request.replyAddress, {
statusCode = 404,
})
end
end
end
end)

View File

@ -74,7 +74,7 @@ Event.on('timer', function(_, timerId)
end)
Event.on('modem_message', function(_, _, dport, dhost, msg, distance)
if dhost == computerId and msg then
if dhost == computerId and type(msg) == 'table' then
local socket = transport.sockets[dport]
if socket and socket.connected then