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 }