github.com/samuelkuklis/utils@v1.0.0/net/port.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package net 18 19 import ( 20 "fmt" 21 "net" 22 "strconv" 23 "strings" 24 ) 25 26 // Protocol is a network protocol support by LocalPort. 27 type Protocol string 28 29 // Constants for valid protocols: 30 const ( 31 TCP Protocol = "TCP" 32 UDP Protocol = "UDP" 33 ) 34 35 // LocalPort represents an IP address and port pair along with a protocol 36 // and potentially a specific IP family. 37 // A LocalPort can be opened and subsequently closed. 38 type LocalPort struct { 39 // Description is an arbitrary string. 40 Description string 41 // IP is the IP address part of a given local port. 42 // If this string is empty, the port binds to all local IP addresses. 43 IP string 44 // If IPFamily is not empty, the port binds only to addresses of this 45 // family. 46 // IF empty along with IP, bind to local addresses of any family. 47 IPFamily IPFamily 48 // Port is the port number. 49 // A value of 0 causes a port to be automatically chosen. 50 Port int 51 // Protocol is the protocol, e.g. TCP 52 Protocol Protocol 53 } 54 55 // NewLocalPort returns a LocalPort instance and ensures IPFamily and IP are 56 // consistent and that the given protocol is valid. 57 func NewLocalPort(desc, ip string, ipFamily IPFamily, port int, protocol Protocol) (*LocalPort, error) { 58 if protocol != TCP && protocol != UDP { 59 return nil, fmt.Errorf("Unsupported protocol %s", protocol) 60 } 61 if ipFamily != IPFamilyUnknown && ipFamily != IPv4 && ipFamily != IPv6 { 62 return nil, fmt.Errorf("Invalid IP family %s", ipFamily) 63 } 64 if ip != "" { 65 parsedIP := ParseIPSloppy(ip) 66 if parsedIP == nil { 67 return nil, fmt.Errorf("invalid ip address %s", ip) 68 } 69 if ipFamily != IPFamilyUnknown { 70 if IPFamily(parsedIP) != ipFamily { 71 return nil, fmt.Errorf("ip address and family mismatch %s, %s", ip, ipFamily) 72 } 73 } 74 } 75 return &LocalPort{Description: desc, IP: ip, IPFamily: ipFamily, Port: port, Protocol: protocol}, nil 76 } 77 78 func (lp *LocalPort) String() string { 79 ipPort := net.JoinHostPort(lp.IP, strconv.Itoa(lp.Port)) 80 return fmt.Sprintf("%q (%s/%s%s)", lp.Description, ipPort, strings.ToLower(string(lp.Protocol)), lp.IPFamily) 81 } 82 83 // Closeable closes an opened LocalPort. 84 type Closeable interface { 85 Close() error 86 } 87 88 // PortOpener can open a LocalPort and allows later closing it. 89 type PortOpener interface { 90 OpenLocalPort(lp *LocalPort) (Closeable, error) 91 } 92 93 type listenPortOpener struct{} 94 95 // ListenPortOpener opens ports by calling bind() and listen(). 96 var ListenPortOpener listenPortOpener 97 98 // OpenLocalPort holds the given local port open. 99 func (l *listenPortOpener) OpenLocalPort(lp *LocalPort) (Closeable, error) { 100 return openLocalPort(lp) 101 } 102 103 func openLocalPort(lp *LocalPort) (Closeable, error) { 104 var socket Closeable 105 hostPort := net.JoinHostPort(lp.IP, strconv.Itoa(lp.Port)) 106 switch lp.Protocol { 107 case TCP: 108 network := "tcp" + string(lp.IPFamily) 109 listener, err := net.Listen(network, hostPort) 110 if err != nil { 111 return nil, err 112 } 113 socket = listener 114 case UDP: 115 network := "udp" + string(lp.IPFamily) 116 addr, err := net.ResolveUDPAddr(network, hostPort) 117 if err != nil { 118 return nil, err 119 } 120 conn, err := net.ListenUDP(network, addr) 121 if err != nil { 122 return nil, err 123 } 124 socket = conn 125 default: 126 return nil, fmt.Errorf("unknown protocol %q", lp.Protocol) 127 } 128 return socket, nil 129 }