package keymgr import "ssh-access-tui/internal/model" // AllocatedForward is a model.Forward together with the local port it will // be bound to on the user's machine. type AllocatedForward struct { model.Forward LocalPort int } // localPortBase is where every local port is handed out from. Forwards are // never given a local port equal to their remote port: many remote ports // worth forwarding are privileged (< 1024 — ssh's own 22 above all), and // binding a listener on a privileged local port requires admin/root rights // the end user does not have and should not need. Even for unprivileged // remote ports, always starting from a fixed high range keeps assignment // simple and collision-free without a "does this port happen to be free // locally" special case. const localPortBase = 20000 // AllocateLocalPorts assigns every forward a local port, all of which the // user opens at once over a single ssh connection (one LocalForward line // per forward — see RenderClientConfig). Ports are handed out sequentially // from localPortBase, so many forwards sharing the same remote port (e.g. // port 22 repeated across ten hosts) each still get a distinct, unprivileged // local port. Assignment is a pure function of the input order, so // re-exporting an unchanged host list reproduces the same local ports. func AllocateLocalPorts(forwards []model.Forward) []AllocatedForward { next := localPortBase out := make([]AllocatedForward, 0, len(forwards)) for _, f := range forwards { out = append(out, AllocatedForward{Forward: f, LocalPort: next}) next++ } return out }