defmodule RNS.PacketHandler do @moduledoc """ Process that handles packets. """ require Logger require RNS.Packet @doc """ Create a new PacketHandler process. This is the function an interface should run. """ def handle(data_fn) do spawn(__MODULE__, :start, [data_fn]) end @doc """ This is the actual handler. It should NEVER BE RUN BY AN INTERFACE as it does not do it in another process. """ @spec start(fun(), RNS.Interface.t(), RNS.Config.transport()) :: :ok def start(packet_fn, interface, transport) do case packet_fn.() do {:ok, raw} -> packet = RNS.Packet.from_raw(raw) packet_hash = RNS.Packet.hash(packet) if not RNS.PacketHashStore.exists?(packet_hash) do RNS.PacketHashStore.add(packet_hash) handle(packet, interface, transport) end {:error, error} -> Logger.warning( "Failed to pre-handle packet on interface #{inspect(interface)}: #{inspect(error)}." ) end end @spec handle(RNS.Packet.t(), RNS.Interface.t(), RNS.Config.transport()) :: :ok def handle(packet, interface, transport) do interface_id = RNS.Interface.id(interface) packet_hops = RNS.Packet.hops(packet) packet = RNS.Packet.packet(packet, hops: packet_hops + 1) case RNS.Packet.packet_type(packet) do :announce -> RNS.Packet.Announce.handle(packet, interface_id) _type -> if RNS.Packet.propagation_type(packet) == :transport do maybe_route(packet, transport, interface) end end end @spec maybe_route(RNS.Packet.t(), RNS.Config.transport(), RNS.Interface.t()) :: :ok def maybe_route(packet, transport, source) do case transport do true -> route(packet, source) false -> destination_hash = RNS.Packet.destination_hash(packet) case RNS.DestinationStore.fetch(destination_hash) do {:ok, destination} -> if RNS.Destination.hops(destination) <= 0 do route(packet, source, destination) end {:error, :not_found} -> :ok end end end @spec route(RNS.Packet.t(), RNS.Interface.t()) :: :ok def route(packet, source) do destination_hash = RNS.Packet.destination_hash(packet) case RNS.DestinationStore.fetch(destination_hash) do {:ok, destination} -> route(packet, source, destination) end end @spec route(RNS.Packet.t(), RNS.Interface.t(), RNS.Destination.t()) :: :ok def route(packet, source, destination) do RNS.Interface.send(packet, RNS.Destination.interface(destination), source) end end