# 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.`: Returns one of the protocol-specified error messages. `` 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.