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  }