48 lines
1.8 KiB
Go
48 lines
1.8 KiB
Go
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 fallback local ports are handed out from once a
|
|
// forward's natural remote port is already taken locally by another
|
|
// forward. Chosen high enough to avoid colliding with well-known ports the
|
|
// user's own machine might already be using.
|
|
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). This is the actual answer to
|
|
// "avoid collisions when forwarding everything simultaneously": many hosts
|
|
// commonly expose the same remote port (e.g. 22, or an app port like 7888
|
|
// repeated across ten hosts), but only one of them can bind to that same
|
|
// port number locally. Each forward keeps its remote port as the local
|
|
// port when that's still free (so the common case reads naturally, e.g.
|
|
// remote 7888 -> local 7888), and falls back to the next free port from
|
|
// localPortBase upward on collision. Assignment is a pure function of the
|
|
// input order and set, so re-exporting an unchanged host list reproduces
|
|
// the same local ports.
|
|
func AllocateLocalPorts(forwards []model.Forward) []AllocatedForward {
|
|
used := make(map[int]bool, len(forwards))
|
|
next := localPortBase
|
|
out := make([]AllocatedForward, 0, len(forwards))
|
|
for _, f := range forwards {
|
|
lp := f.Port
|
|
if lp <= 0 || lp > 65535 || used[lp] {
|
|
for used[next] {
|
|
next++
|
|
}
|
|
lp = next
|
|
next++
|
|
}
|
|
used[lp] = true
|
|
out = append(out, AllocatedForward{Forward: f, LocalPort: lp})
|
|
}
|
|
return out
|
|
}
|