github.com/pwn-term/docker@v0.0.0-20210616085119-6e977cce2565/libnetwork/drivers/windows/port_mapping.go (about)

     1  // +build windows
     2  
     3  package windows
     4  
     5  import (
     6  	"bytes"
     7  	"errors"
     8  	"fmt"
     9  	"net"
    10  
    11  	"github.com/docker/libnetwork/portmapper"
    12  	"github.com/docker/libnetwork/types"
    13  	"github.com/ishidawataru/sctp"
    14  	"github.com/sirupsen/logrus"
    15  )
    16  
    17  const (
    18  	maxAllocatePortAttempts = 10
    19  )
    20  
    21  // ErrUnsupportedAddressType is returned when the specified address type is not supported.
    22  type ErrUnsupportedAddressType string
    23  
    24  func (uat ErrUnsupportedAddressType) Error() string {
    25  	return fmt.Sprintf("unsupported address type: %s", string(uat))
    26  }
    27  
    28  // AllocatePorts allocates ports specified in bindings from the portMapper
    29  func AllocatePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding, containerIP net.IP) ([]types.PortBinding, error) {
    30  	bs := make([]types.PortBinding, 0, len(bindings))
    31  	for _, c := range bindings {
    32  		b := c.GetCopy()
    33  		if err := allocatePort(portMapper, &b, containerIP); err != nil {
    34  			// On allocation failure, release previously allocated ports. On cleanup error, just log a warning message
    35  			if cuErr := ReleasePorts(portMapper, bs); cuErr != nil {
    36  				logrus.Warnf("Upon allocation failure for %v, failed to clear previously allocated port bindings: %v", b, cuErr)
    37  			}
    38  			return nil, err
    39  		}
    40  		bs = append(bs, b)
    41  	}
    42  	return bs, nil
    43  }
    44  
    45  func allocatePort(portMapper *portmapper.PortMapper, bnd *types.PortBinding, containerIP net.IP) error {
    46  	var (
    47  		host net.Addr
    48  		err  error
    49  	)
    50  
    51  	// Windows does not support a host ip for port bindings (this is validated in ConvertPortBindings()).
    52  	// If the HostIP is nil, force it to be 0.0.0.0 for use as the key in portMapper.
    53  	if bnd.HostIP == nil {
    54  		bnd.HostIP = net.IPv4zero
    55  	}
    56  
    57  	// Store the container interface address in the operational binding
    58  	bnd.IP = containerIP
    59  
    60  	// Adjust HostPortEnd if this is not a range.
    61  	if bnd.HostPortEnd == 0 {
    62  		bnd.HostPortEnd = bnd.HostPort
    63  	}
    64  
    65  	// Construct the container side transport address
    66  	container, err := bnd.ContainerAddr()
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	// Try up to maxAllocatePortAttempts times to get a port that's not already allocated.
    72  	for i := 0; i < maxAllocatePortAttempts; i++ {
    73  		if host, err = portMapper.MapRange(container, bnd.HostIP, int(bnd.HostPort), int(bnd.HostPortEnd), false); err == nil {
    74  			break
    75  		}
    76  		// There is no point in immediately retrying to map an explicitly chosen port.
    77  		if bnd.HostPort != 0 {
    78  			logrus.Warnf("Failed to allocate and map port %d-%d: %s", bnd.HostPort, bnd.HostPortEnd, err)
    79  			break
    80  		}
    81  		logrus.Warnf("Failed to allocate and map port: %s, retry: %d", err, i+1)
    82  	}
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	// Save the host port (regardless it was or not specified in the binding)
    88  	switch netAddr := host.(type) {
    89  	case *net.TCPAddr:
    90  		bnd.HostPort = uint16(host.(*net.TCPAddr).Port)
    91  		break
    92  	case *net.UDPAddr:
    93  		bnd.HostPort = uint16(host.(*net.UDPAddr).Port)
    94  		break
    95  	case *sctp.SCTPAddr:
    96  		bnd.HostPort = uint16(host.(*sctp.SCTPAddr).Port)
    97  		break
    98  	default:
    99  		// For completeness
   100  		return ErrUnsupportedAddressType(fmt.Sprintf("%T", netAddr))
   101  	}
   102  	//Windows does not support host port ranges.
   103  	bnd.HostPortEnd = bnd.HostPort
   104  	return nil
   105  }
   106  
   107  // ReleasePorts releases ports specified in bindings from the portMapper
   108  func ReleasePorts(portMapper *portmapper.PortMapper, bindings []types.PortBinding) error {
   109  	var errorBuf bytes.Buffer
   110  
   111  	// Attempt to release all port bindings, do not stop on failure
   112  	for _, m := range bindings {
   113  		if err := releasePort(portMapper, m); err != nil {
   114  			errorBuf.WriteString(fmt.Sprintf("\ncould not release %v because of %v", m, err))
   115  		}
   116  	}
   117  
   118  	if errorBuf.Len() != 0 {
   119  		return errors.New(errorBuf.String())
   120  	}
   121  	return nil
   122  }
   123  
   124  func releasePort(portMapper *portmapper.PortMapper, bnd types.PortBinding) error {
   125  	// Construct the host side transport address
   126  	host, err := bnd.HostAddr()
   127  	if err != nil {
   128  		return err
   129  	}
   130  	return portMapper.Unmap(host)
   131  }