trivial v0.1.0 Trivial.Server behaviour View Source
A genserver which handles data packet transfers between the TFTP service
and hungry clients. By being spawned off of Trivial.Daemon
after accept,
it allows transactions to be concurrent and asynchronous.
Generally, you should not be initiating from this module, except possibly for testing puroposes.
This module also serves as a behaviour module defining the API that you should use to service TFTP transactions.
Example
The following example shows how to set up a TFTP module which can
transmit both templated and file data. Note that in general, you should
not use stateful reading techinques in the read/5
callback since UDP
has no delivery guarantees and the client may re-request a data block,
meaning that your data transmission may be nonlinear.
defmodule MyTftpModule do
@behaviour Trivial.Server
@impl true
# forbids transactions from the evil ip address 42.42.42.42
def init(_, {42, 42, 42, 42}, _), do: {:error, :eacces}
# forbids transactions to unwanted files
def init("secret.txt", _client, _), do: {:error, :eacces}
# set up a templated transaction
def init("/templated", _client, data) do
str = "template for #{inet.ntoa client}"
size = :erlang.size(str)
{:ok, {str, size}}
end
# set up file transaction
def init(file, _client, data) do
bin = File.read!(file)
size = :erlang.size(bin)
{:ok, {bin, size}}
end
@impl true
def read(_request, pos, len, _client, data = {str, size}) when pos > size do
:done
end
def read(_request, pos, len, _client, data = {str, size}) pos + len > size do
{:ok, :erlang.binary_part(str, pos, size - pos), data}
end
def read(_request, pos, len, _client, data = {str, size}) do
{:ok, :erlang.binary_part(str, pos, len), data}
end
@impl true
def tsize(_request, _client, data = {_, size}) do
{:ok, size, data}
end
end
Link to this section Summary
Functions
Returns a specification to start this module under a supervisor.
used to retrieve the open port that is being used for the transaction. This
is synonomyous to TID
in RFC 1350.
starts up a TFTP server that wraps a single set of connection details.
Callbacks
called after the TFTP transaction request has been made to to the server, but before any further messages have been passed between the client and the server. Most stateful setup and teardown should occur here.
called when the TFTP server needs to deliver another data packet to the client.
called if the TFTP client has requested to know the size of the content ahead of time.
Link to this section Functions
child_spec(init_arg) View Source
Returns a specification to start this module under a supervisor.
See Supervisor
.
port(svr)
View Source
port(GenServer.server()) :: :inet.port_number()
port(GenServer.server()) :: :inet.port_number()
used to retrieve the open port that is being used for the transaction. This
is synonomyous to TID
in RFC 1350.
start_link(conn)
View Source
start_link(Trivial.Conn.t()) :: GenServer.on_start()
start_link(Trivial.Conn.t()) :: GenServer.on_start()
starts up a TFTP server that wraps a single set of connection details.
Link to this section Callbacks
init(request, client, data)
View Source
init(request :: Path.t(), client :: :inet.address(), data) ::
{:ok, data} | :error | {:error, Trivial.Packet.error_codes()}
when data: term()
init(request :: Path.t(), client :: :inet.address(), data) :: {:ok, data} | :error | {:error, Trivial.Packet.error_codes()} when data: term()
called after the TFTP transaction request has been made to to the server, but before any further messages have been passed between the client and the server. Most stateful setup and teardown should occur here.
read(request, position, size, client, data)
View Source
read(
request :: Path.t(),
position :: non_neg_integer(),
size :: non_neg_integer(),
client :: :inet.address(),
data
) :: {:ok, binary(), data} | :done | {:error, type :: atom(), String.t(), data}
when data: term()
read( request :: Path.t(), position :: non_neg_integer(), size :: non_neg_integer(), client :: :inet.address(), data ) :: {:ok, binary(), data} | :done | {:error, type :: atom(), String.t(), data} when data: term()
called when the TFTP server needs to deliver another data packet to the client.
tsize(request, client, data)
View Source
tsize(request :: Path.t(), client :: :inet.address(), data) ::
{:ok, non_neg_integer(), data} | {:error, reason :: atom(), String.t(), data}
when data: term()
tsize(request :: Path.t(), client :: :inet.address(), data) :: {:ok, non_neg_integer(), data} | {:error, reason :: atom(), String.t(), data} when data: term()
called if the TFTP client has requested to know the size of the content ahead of time.
Your read/5
calls should respect this value as a maximum size limit
to your content payload