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

Returns a specification to start this module under a supervisor.

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

Link to this section Callbacks

Link to this callback

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

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.

Link to this callback

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

called when the TFTP server needs to deliver another data packet to the client.

Link to this callback

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

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