github.com/goern/docker@v1.9.0-rc1/pkg/nat/nat.go (about) 1 package nat 2 3 // nat is a convenience package for docker's manipulation of strings describing 4 // network ports. 5 6 import ( 7 "fmt" 8 "net" 9 "strconv" 10 "strings" 11 12 "github.com/docker/docker/pkg/parsers" 13 ) 14 15 const ( 16 // portSpecTemplate is the expected format for port specifications 17 portSpecTemplate = "ip:hostPort:containerPort" 18 ) 19 20 // PortBinding represents a binding between a Host IP address and a Host Port 21 type PortBinding struct { 22 // HostIP is the host IP Address 23 HostIP string `json:"HostIp"` 24 // HostPort is the host port number 25 HostPort string 26 } 27 28 // PortMap is a collection of PortBinding indexed by Port 29 type PortMap map[Port][]PortBinding 30 31 // PortSet is a collection of structs indexed by Port 32 type PortSet map[Port]struct{} 33 34 // Port is a string containing port number and protocol in the format "80/tcp" 35 type Port string 36 37 // NewPort creates a new instance of a Port given a protocol and port number or port range 38 func NewPort(proto, port string) (Port, error) { 39 // Check for parsing issues on "port" now so we can avoid having 40 // to check it later on. 41 42 portStartInt, portEndInt, err := ParsePortRange(port) 43 if err != nil { 44 return "", err 45 } 46 47 if portStartInt == portEndInt { 48 return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil 49 } 50 return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil 51 } 52 53 // ParsePort parses the port number string and returns an int 54 func ParsePort(rawPort string) (int, error) { 55 if len(rawPort) == 0 { 56 return 0, nil 57 } 58 port, err := strconv.ParseUint(rawPort, 10, 16) 59 if err != nil { 60 return 0, err 61 } 62 return int(port), nil 63 } 64 65 // ParsePortRange parses the port range string and returns start/end ints 66 func ParsePortRange(rawPort string) (int, int, error) { 67 if len(rawPort) == 0 { 68 return 0, 0, nil 69 } 70 start, end, err := parsers.ParsePortRange(rawPort) 71 if err != nil { 72 return 0, 0, err 73 } 74 return int(start), int(end), nil 75 } 76 77 // Proto returns the protocol of a Port 78 func (p Port) Proto() string { 79 proto, _ := SplitProtoPort(string(p)) 80 return proto 81 } 82 83 // Port returns the port number of a Port 84 func (p Port) Port() string { 85 _, port := SplitProtoPort(string(p)) 86 return port 87 } 88 89 // Int returns the port number of a Port as an int 90 func (p Port) Int() int { 91 portStr := p.Port() 92 if len(portStr) == 0 { 93 return 0 94 } 95 96 // We don't need to check for an error because we're going to 97 // assume that any error would have been found, and reported, in NewPort() 98 port, _ := strconv.ParseUint(portStr, 10, 16) 99 return int(port) 100 } 101 102 // Range returns the start/end port numbers of a Port range as ints 103 func (p Port) Range() (int, int, error) { 104 return ParsePortRange(p.Port()) 105 } 106 107 // SplitProtoPort splits a port in the format of proto/port 108 func SplitProtoPort(rawPort string) (string, string) { 109 parts := strings.Split(rawPort, "/") 110 l := len(parts) 111 if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 { 112 return "", "" 113 } 114 if l == 1 { 115 return "tcp", rawPort 116 } 117 if len(parts[1]) == 0 { 118 return "tcp", parts[0] 119 } 120 return parts[1], parts[0] 121 } 122 123 func validateProto(proto string) bool { 124 for _, availableProto := range []string{"tcp", "udp"} { 125 if availableProto == proto { 126 return true 127 } 128 } 129 return false 130 } 131 132 // ParsePortSpecs receives port specs in the format of ip:public:private/proto and parses 133 // these in to the internal types 134 func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) { 135 var ( 136 exposedPorts = make(map[Port]struct{}, len(ports)) 137 bindings = make(map[Port][]PortBinding) 138 ) 139 140 for _, rawPort := range ports { 141 proto := "tcp" 142 143 if i := strings.LastIndex(rawPort, "/"); i != -1 { 144 proto = rawPort[i+1:] 145 rawPort = rawPort[:i] 146 } 147 if !strings.Contains(rawPort, ":") { 148 rawPort = fmt.Sprintf("::%s", rawPort) 149 } else if len(strings.Split(rawPort, ":")) == 2 { 150 rawPort = fmt.Sprintf(":%s", rawPort) 151 } 152 153 parts, err := parsers.PartParser(portSpecTemplate, rawPort) 154 if err != nil { 155 return nil, nil, err 156 } 157 158 var ( 159 containerPort = parts["containerPort"] 160 rawIP = parts["ip"] 161 hostPort = parts["hostPort"] 162 ) 163 164 if rawIP != "" && net.ParseIP(rawIP) == nil { 165 return nil, nil, fmt.Errorf("Invalid ip address: %s", rawIP) 166 } 167 if containerPort == "" { 168 return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort) 169 } 170 171 startPort, endPort, err := parsers.ParsePortRange(containerPort) 172 if err != nil { 173 return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort) 174 } 175 176 var startHostPort, endHostPort uint64 = 0, 0 177 if len(hostPort) > 0 { 178 startHostPort, endHostPort, err = parsers.ParsePortRange(hostPort) 179 if err != nil { 180 return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort) 181 } 182 } 183 184 if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) { 185 // Allow host port range iff containerPort is not a range. 186 // In this case, use the host port range as the dynamic 187 // host port range to allocate into. 188 if endPort != startPort { 189 return nil, nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort) 190 } 191 } 192 193 if !validateProto(strings.ToLower(proto)) { 194 return nil, nil, fmt.Errorf("Invalid proto: %s", proto) 195 } 196 197 for i := uint64(0); i <= (endPort - startPort); i++ { 198 containerPort = strconv.FormatUint(startPort+i, 10) 199 if len(hostPort) > 0 { 200 hostPort = strconv.FormatUint(startHostPort+i, 10) 201 } 202 // Set hostPort to a range only if there is a single container port 203 // and a dynamic host port. 204 if startPort == endPort && startHostPort != endHostPort { 205 hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10)) 206 } 207 port, err := NewPort(strings.ToLower(proto), containerPort) 208 if err != nil { 209 return nil, nil, err 210 } 211 if _, exists := exposedPorts[port]; !exists { 212 exposedPorts[port] = struct{}{} 213 } 214 215 binding := PortBinding{ 216 HostIP: rawIP, 217 HostPort: hostPort, 218 } 219 bslice, exists := bindings[port] 220 if !exists { 221 bslice = []PortBinding{} 222 } 223 bindings[port] = append(bslice, binding) 224 } 225 } 226 return exposedPorts, bindings, nil 227 }