Files
stftpd/doc/lua_api.md

126 lines
4.6 KiB
Markdown

# Overview
> :warning:
> Not all of this is presently implemented. See the [README](../README.md) for details
> on implementation status.
The lua script must have the following form:
```lua
-- 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:
```lua
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 directory
* `resource.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 to `resource.ERROR.FileAlreadyExists`
* `resource.ERROR(message)`: Returns a free-form error message
* `resource.ERROR.<type>`: Returns one of the protocol-specified error messages.
`<type>` may be one of the following:
* `Unknown`
* `FileNotFound`
* `PermissionDenied`
* `DiskFull`
* `IllegalOperation`
* `FileAlreadyExists`
* `NoSuchUser`
## 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, use `resource.HTTP`.
* `redis.CMD(command, args...)`: Performs a redis request. Returns the datastructure
returned by Redis, without interpretation. On error, returns `nil, 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 a `stftpd.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/96` is equivalent to `0.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:
```lua
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.