github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/netutil/addr/addr.go (about) 1 // Copyright 2014 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package addr 12 13 import ( 14 "fmt" 15 "net" 16 "strconv" 17 "strings" 18 19 "github.com/cockroachdb/errors" 20 "github.com/spf13/pflag" 21 ) 22 23 // AddrWithDefaultLocalhost returns addr with the host set 24 // to localhost if it is empty. 25 func AddrWithDefaultLocalhost(addr string) (string, error) { 26 host, port, err := net.SplitHostPort(addr) 27 if err != nil { 28 return "", err 29 } 30 if host == "" { 31 host = "localhost" 32 } 33 return net.JoinHostPort(host, port), nil 34 } 35 36 // SplitHostPort is like net.SplitHostPort however it supports 37 // addresses without a port number. In that case, the provided port 38 // number is used. 39 func SplitHostPort(v string, defaultPort string) (addr string, port string, err error) { 40 addr, port, err = net.SplitHostPort(v) 41 if err != nil { 42 var aerr *net.AddrError 43 if errors.As(err, &aerr) { 44 if strings.HasPrefix(aerr.Err, "too many colons") { 45 // Maybe this was an IPv6 address using the deprecated syntax 46 // without '[...]'? Try that to help the user with a hint. 47 // Note: the following is valid even if defaultPort is empty. 48 // (An empty port number is always a valid listen address.) 49 maybeAddr := "[" + v + "]:" + defaultPort 50 addr, port, err = net.SplitHostPort(maybeAddr) 51 if err == nil { 52 err = errors.WithHintf( 53 errors.Newf("invalid address format: %q", v), 54 "enclose IPv6 addresses within [...], e.g. \"[%s]\"", v) 55 } 56 } else if strings.HasPrefix(aerr.Err, "missing port") { 57 // It's inconvenient that SplitHostPort doesn't know how to ignore 58 // a missing port number. Oh well. 59 addr, port, err = net.SplitHostPort(v + ":" + defaultPort) 60 } 61 } 62 } 63 if err == nil && port == "" { 64 port = defaultPort 65 } 66 return addr, port, err 67 } 68 69 type addrSetter struct { 70 addr *string 71 port *string 72 } 73 74 // NewAddrSetter creates a new pflag.Value raps a address/port 75 // configuration option pair and enables setting them both with a 76 // single command-line flag. 77 func NewAddrSetter(hostOption, portOption *string) pflag.Value { 78 return &addrSetter{addr: hostOption, port: portOption} 79 } 80 81 // String implements the pflag.Value interface. 82 func (a addrSetter) String() string { 83 return net.JoinHostPort(*a.addr, *a.port) 84 } 85 86 // Type implements the pflag.Value interface. 87 func (a addrSetter) Type() string { return "<addr/host>[:<port>]" } 88 89 // Set implements the pflag.Value interface. 90 func (a addrSetter) Set(v string) error { 91 addr, port, err := SplitHostPort(v, *a.port) 92 if err != nil { 93 return err 94 } 95 *a.addr = addr 96 *a.port = port 97 return nil 98 } 99 100 type portRangeSetter struct { 101 lower *int 102 upper *int 103 } 104 105 // NewPortRangeSetter creates a new pflag.Value that allows setting a 106 // lower and upper bound of a port range with a single setting. 107 func NewPortRangeSetter(lower, upper *int) pflag.Value { 108 return portRangeSetter{lower: lower, upper: upper} 109 } 110 111 // String implements the pflag.Value interface. 112 func (a portRangeSetter) String() string { 113 return fmt.Sprintf("%d-%d", *a.lower, *a.upper) 114 } 115 116 // Type implements the pflag.Value interface. 117 func (a portRangeSetter) Type() string { return "<lower>-<upper>" } 118 119 // Set implements the pflag.Value interface. 120 func (a portRangeSetter) Set(v string) error { 121 parts := strings.Split(v, "-") 122 if len(parts) > 2 { 123 return errors.New("invalid port range: too many parts") 124 } 125 126 if len(parts) < 2 || parts[1] == "" { 127 return errors.New("invalid port range: too few parts") 128 } 129 130 lower, err := strconv.Atoi(parts[0]) 131 if err != nil { 132 return errors.Wrap(err, "invalid port range") 133 } 134 135 upper, err := strconv.Atoi(parts[1]) 136 if err != nil { 137 return errors.Wrap(err, "invalid port range") 138 } 139 140 if lower > upper { 141 return errors.Newf("invalid port range: lower bound (%d) > upper bound (%d)", lower, upper) 142 } 143 *a.lower = lower 144 *a.upper = upper 145 146 return nil 147 }