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
85 lines
2.3 KiB
Go
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
|
|
}
|