diff --git a/README.md b/README.md index 6e3d7a0..7a1651c 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,12 @@ This produces an executable named `spore` in the project directory. ## Quickstart ### Server (choose a public range) ```bash -./spore server --min-port 20000 --max-port 21000 --bind-addr 0.0.0.0 +./spore server --min-port 20000 --max-port 21000 --bind-addr 0.0.0.0 [--control-port 7835] ``` ### Client (forward local 3000; let server assign a port) ```bash -./spore local --local-host 127.0.0.1 --local-port 3000 --to --port 0 +./spore local --local-host 127.0.0.1 --local-port 3000 --to --port 0 [--control-port 7835] ``` Spore prints the assigned public port (for example, `listening at :20345`). @@ -34,11 +34,11 @@ python3 -m http.server 25565 ``` Terminal B (server): ```bash -./spore server --min-port 20000 --max-port 21000 --bind-addr 127.0.0.1 +./spore server --min-port 20000 --max-port 21000 --bind-addr 127.0.0.1 [--control-port 7835] ``` Terminal C (client): ```bash -./spore local --local-host 127.0.0.1 --local-port 25565 --to 127.0.0.1 --port 0 +./spore local --local-host 127.0.0.1 --local-port 25565 --to 127.0.0.1 --port 0 [--control-port 7835] ``` Terminal D (access through tunnel): ```bash @@ -48,8 +48,8 @@ curl -v 127.0.0.1:/ ## Authentication (optional) Provide the same secret on both sides to restrict access: ```bash -./spore server --secret SECRET -./spore local --local-port 3000 --to --secret SECRET +./spore server --secret SECRET [--control-port 7835] +./spore local --local-port 3000 --to --secret SECRET [--control-port 7835] ``` ## Interoperability @@ -60,9 +60,9 @@ Spore is designed to speak the same control protocol as Bore. You can mix Rust B - Pending inbound connections are stored for up to 10 seconds; if the client does not accept within that window, they are dropped. ## Troubleshooting -- "address in use" starting server: another process is listening on TCP 7835. Stop it or choose a different control port (not yet configurable in Spore). -- Client exits with `:eof`: ensure server is reachable at `--to`, secrets match (or are omitted on both), and TCP 7835 is open. -- Repeated "removed stale connection": ensure the client is running and that a remote connection arrives soon after the client starts (Spore holds pending connections for 10s). +- "address in use" starting server: another process is listening on the control port. Use `--control-port` to choose a different one or stop the existing process. +- Client exits with `:eof`: ensure server is reachable at `--to`, secrets match (or are omitted on both), and the control port is open. +- Repeated "removed stale connection": ensure the client is running and a remote connection arrives soon after the client starts (Spore holds pending connections for 10s). ## License MIT, following the upstream project. diff --git a/config/config.exs b/config/config.exs index 477c907..17b3c71 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,3 +1,12 @@ import Config config :logger, level: :info + +# Allow overriding the control port via env var SPORE_CONTROL_PORT +spore_control_port = + case System.get_env("SPORE_CONTROL_PORT") do + nil -> 7835 + str -> String.to_integer(str) + end + +config :spore, control_port: spore_control_port diff --git a/lib/spore/cli.ex b/lib/spore/cli.ex index adb5142..78f4ed5 100644 --- a/lib/spore/cli.ex +++ b/lib/spore/cli.ex @@ -20,7 +20,8 @@ defmodule Spore.CLI do local_host: :string, to: :string, port: :integer, - secret: :string + secret: :string, + control_port: :integer ], aliases: [p: :port] ) @@ -30,6 +31,9 @@ defmodule Spore.CLI do to = Keyword.fetch!(opts, :to) port = Keyword.get(opts, :port, 0) secret = Keyword.get(opts, :secret, nil) + control_port = Keyword.get(opts, :control_port, nil) + + if control_port, do: Application.put_env(:spore, :control_port, control_port) case Spore.Client.new(local_host, local_port, to, port, secret) do {:ok, client} -> @@ -51,7 +55,8 @@ defmodule Spore.CLI do max_port: :integer, secret: :string, bind_addr: :string, - bind_tunnels: :string + bind_tunnels: :string, + control_port: :integer ] ) @@ -60,6 +65,9 @@ defmodule Spore.CLI do secret = Keyword.get(opts, :secret, nil) bind_addr = Keyword.get(opts, :bind_addr, "0.0.0.0") bind_tunnels = Keyword.get(opts, :bind_tunnels, nil) + control_port = Keyword.get(opts, :control_port, nil) + + if control_port, do: Application.put_env(:spore, :control_port, control_port) case Spore.Server.listen( min_port: min_port, @@ -76,8 +84,8 @@ defmodule Spore.CLI do defp usage(io) do IO.puts(io, """ Usage: - spore local --local-port --to [--local-host HOST] [--port PORT] [--secret SECRET] - spore server [--min-port N] [--max-port N] [--secret SECRET] [--bind-addr IP] [--bind-tunnels IP] + spore local --local-port --to [--local-host HOST] [--port PORT] [--secret SECRET] [--control-port N] + spore server [--min-port N] [--max-port N] [--secret SECRET] [--bind-addr IP] [--bind-tunnels IP] [--control-port N] """) end end diff --git a/lib/spore/shared.ex b/lib/spore/shared.ex index dfb08e4..3aa3c95 100644 --- a/lib/spore/shared.ex +++ b/lib/spore/shared.ex @@ -10,8 +10,10 @@ defmodule Spore.Shared do @max_frame_length 256 @network_timeout_ms 3_000 - @doc "TCP port used for control connections with the server." - def control_port, do: @control_port + @doc "TCP port used for control connections with the server. Can be overridden via Application env :spore, :control_port." + def control_port do + Application.get_env(:spore, :control_port, @control_port) + end @doc "Default timeout for initial network operations (ms)." def network_timeout_ms, do: @network_timeout_ms