Files
stftpd/doc/lua_api.md
2023-11-18 17:06:10 +01:00

3.9 KiB

Overview

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 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