102 lines
3.9 KiB
Markdown
102 lines
3.9 KiB
Markdown
# Overview
|
|
|
|
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` |