-
Notifications
You must be signed in to change notification settings - Fork 1
/
ports.go
115 lines (94 loc) · 2.78 KB
/
ports.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright © 2017,2020 Pennock Tech, LLC.
// All rights reserved, except as granted under license.
// Licensed per file LICENSE.txt
package main
// I like the Perl idiom for ports of `name(number)`, to look up the name in
// services and if not found, then fall back to using the given number.
import (
"errors"
"fmt"
"net"
"regexp"
"strconv"
)
const minPort = 1
const maxPort = 65535
var portRE *regexp.Regexp
func init() {
portRE = regexp.MustCompile(`^([A-Za-z0-9._-]+)(?:\((\d+)\))?$`)
}
// Parse a port specification and return a valid port number, or an error.
// Ports are bound to [1,65535]. Specification can be a numeric port, or a
// service name, or "service(number)" to use the name if known, falling back
// to the supplied port. Numbers can be in any base recognized by Golang as
// a spec spec.
func PortParse(spec string) (int, error) {
u64, err := strconv.ParseUint(spec, 0, 16)
if err == nil {
if u64 < minPort || maxPort < u64 {
return 0, fmt.Errorf("invalid port number, out of range: %d", u64)
}
return int(u64), nil
}
matches := portRE.FindStringSubmatch(spec)
if matches == nil {
return 0, fmt.Errorf("unable to parse: %q", spec)
}
if len(matches) < 2 {
panic("bad matches array")
}
port, nameErr := net.LookupPort("tcp", matches[1])
if nameErr == nil {
return port, nil
}
if len(matches) < 3 {
return 0, nameErr
}
u64, err = strconv.ParseUint(matches[2], 0, 16)
if err == nil {
if u64 < minPort || maxPort < u64 {
return 0, fmt.Errorf("%s and %d also out of range", nameErr, u64)
}
return int(u64), nil
}
return 0, fmt.Errorf("%s and %s", nameErr, err)
}
func HostnamePortFrom(spec string) (string, int, error) {
h, p, err := net.SplitHostPort(spec)
if err == nil {
p2, err := PortParse(p)
return h, p2, err
}
var aErr *net.AddrError
if errors.As(err, &aErr) {
// either too many colons or missing port; assume missing port, let
// error out later, since there's no way to tell without string
// matching.
return spec, opts.defaultPortInt, nil
}
return "", 0, err
}
func HostnameMaybePortFrom(spec string) (string, string, error) {
h, p, err := net.SplitHostPort(spec)
if err == nil {
p2, err := PortParse(p)
return h, strconv.Itoa(p2), err
}
var aErr *net.AddrError
if errors.As(err, &aErr) {
// either too many colons or missing port; assume missing port, let
// error out later, since there's no way to tell without string
// matching.
return spec, "", nil
}
return "", "", err
}
func HostPortWithDefaultPort(spec string, defaultPort string) string {
_, _, err := net.SplitHostPort(spec)
if err == nil {
return spec
}
// Similarly, there might be other errors than missing port here; figure
// out if we want to handle those, or let error occur later.
return net.JoinHostPort(spec, defaultPort)
}