4.6 KiB
Overview
⚠️ Not all of this is presently implemented. See the README for details on implementation status.
The lua script must have the following form:
-- Do whatever setup is necessary for the handler.
local function handle(path, client, size)
-- Decide what to do with the request. The following
-- behaves like a traditional TFTP server.
return resource.FILE(path)
end
return handle()
For simple scripts, an alternative form is also possible:
path, client, size = table.unpack(arg)
-- For a script this short, the following line is optional. A path
-- consisting of a single null byte is used to determine whether
-- the script is the "simple" form, and thus you can exit early.
if path == "\0" then return resource.ERR.Unknown end
return resource.FILE(path)
The latter form is shorter, but prevents any pre-calculation or initialization steps, so generally the function approach is preferred.
Arguments
Whether a function or a bare script is used, the following arguments are given:
path: The path that is requested.client: Information about the client connection. This is a table containing the following fields:address: The client address, as an IpAddress object.for_write: true if this is a write request
size: nil for reads, the declared object size (if any) for writes
Available functions
Resource returns
resource.FILE(path): Reads or writes the given path in the filesystem. This is interpreted relative to the configured root directory, and does not allow access outside of that directoryresource.HTTP(url): Fetch a given HTTP URL. Reads act as an HTTP GET request, whereas writes POST the body to the server. While this may seem to be a security risk, generally the ability to speak TFTP implies the ability to send arbitrary network traffic on a nearby network segment.resource.DATA(content): Simply sends the given string as the file content. For write requests, equivalent toresource.ERROR.FileAlreadyExistsresource.ERROR(message): Returns a free-form error messageresource.ERROR.<type>: Returns one of the protocol-specified error messages.<type>may be one of the following:UnknownFileNotFoundPermissionDeniedDiskFullIllegalOperationFileAlreadyExistsNoSuchUser
Network requests
http.GET(url): Fetches the content of the given URL as a string. This will throw an error if more than 1MiB is returned; if possible, useresource.HTTP.redis.CMD(command, args...): Performs a redis request. Returns the datastructure returned by Redis, without interpretation. On error, returnsnil, err. If no Redis server is configured, this will always throw an error.
Data types
stftpd.Cidr
Contains a CIDR network (i.e., a network address and a netmask).
Can be constructed using either stftpd.Cidr(address, prefix) or stftpd.Cidr("address/prefix").
Available fields are:
.addr: The network address as astftpd.IpAddr.prefix: The network prefix, as a number of bits.
Available methods include:
:contains(address): Returns true if the address is contained in this network. Note that IPv4 address will be matched against an IPv6 network as if they were mapped. (i.e.,::ffff:0/96is equivalent to0.0.0.0/0) A similar process is used to match an IPv4 network against an IPv6 address.
stftpd.IpAddr
Contains an IP address (whether v4 or v6). This can be constructed using, e.g.
stftpd.IpAddr("1.2.3.4"). Further, a string containing an IP address can be used
anywhere an IpAddr is expected.
It can be converted to text via tostring, or to bytes using .bytes. Further,
individual bytes of the address can be accessed or modified using indexing notation.
You can test the version using either .version, which returns either 4 or 6, or
.is_v4 and .is_v6. If you need the IPv6-mapped version of a v4 address, (i.e.,
::ffff:0.0.0.0), this is available through .to_v6
This is probably most useful in conjunction with stftpd.Cidr
Examples
Probably the most useful starting point for your own script will be the following:
return function(path, client, size)
if path:match("^/?^/?https?://") then
if path:sub(1,1) == "/" then
path = path:sub(2)
end
return resource.HTTP(path)
end
return resource.FILE(path)
end
This detects HTTP (or HTTPS) urls, and transparently proxies them to the appropriate HTTP server.
Some TFTP clients automatically insert a leading /, so this function strips it if given.
If the given path doesn't look like an HTTP URL, this example tries to handle it as a file.