mukan-ignite/ignite/pkg/availableport/availableport.go
Mukan Erkin Törük c32551b6f7
Some checks failed
Docs Deploy / build_and_deploy (push) Has been cancelled
Generate Docs / cli (push) Has been cancelled
Generate Config Doc / cli (push) Has been cancelled
Go formatting / go-formatting (push) Has been cancelled
Check links / markdown-link-check (push) Has been cancelled
Integration / pre-test (push) Has been cancelled
Integration / test on (push) Has been cancelled
Integration / status (push) Has been cancelled
Lint / Lint Go code (push) Has been cancelled
Test / test (ubuntu-latest) (push) Has been cancelled
refactor: replace all github.com upstream refs with git.cw.tr/mukan-network
2026-05-11 03:36:24 +03:00

85 lines
2.3 KiB
Go

package availableport
import (
"crypto/rand"
"fmt"
"math/big"
"net"
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/errors"
"git.cw.tr/mukan-network/mukan-ignite/ignite/pkg/safeconverter"
)
type availablePortOptions struct {
minPort uint
maxPort uint
}
type Options func(o *availablePortOptions)
func WithMaxPort(maxPort uint) Options {
return func(o *availablePortOptions) {
o.maxPort = maxPort
}
}
func WithMinPort(minPort uint) Options {
return func(o *availablePortOptions) {
o.minPort = minPort
}
}
// Find finds n number of unused ports.
// it is not guaranteed that these ports will not be allocated to
// another program in the time of calling Find().
func Find(n uint, options ...Options) (ports []uint, err error) {
// Defining them before so we can set a value depending on the AvailablePortOptions
opts := availablePortOptions{
minPort: 44000,
maxPort: 55000,
}
for _, apply := range options {
apply(&opts)
}
// If the number of ports required is bigger than the range, this stops it
if opts.maxPort < opts.minPort {
return nil, errors.Errorf("invalid ports range: max < min (%d < %d)", opts.maxPort, opts.minPort)
}
// If the number of ports required is bigger than the range, this stops it
if n > (opts.maxPort - opts.minPort) {
return nil, errors.Errorf("invalid amount of ports requested: limit is %d", opts.maxPort-opts.minPort)
}
// Marker to point if a port is already added in the list
registered := make(map[uint]bool)
i := safeconverter.ToInt[uint](n)
for len(registered) < i {
// Greater or equal to min and lower than max
totalPorts := opts.maxPort - opts.minPort + 1
randomPort, _ := rand.Int(rand.Reader, big.NewInt(safeconverter.ToInt64[uint](totalPorts)))
port := uint(randomPort.Uint64()) + opts.minPort
conn, err := net.Dial("tcp", fmt.Sprintf(":%d", port))
// if there is an error, this might mean that no one is listening from this port
// which is what we need.
if err == nil {
conn.Close()
continue
}
if conn != nil {
defer conn.Close()
}
// if the port is already registered we skip it to the next one
// otherwise it's added to the ports list and pointed in our map
if registered[port] {
continue
}
ports = append(ports, port)
registered[port] = true
}
return ports, nil
}