github.com/torfuzx/docker@v1.8.1/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 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 portInt, err := ParsePort(port) 43 if err != nil { 44 return "", err 45 } 46 47 return Port(fmt.Sprintf("%d/%s", portInt, proto)), nil 48 } 49 50 // ParsePort parses the port number string and returns an int 51 func ParsePort(rawPort string) (int, error) { 52 if len(rawPort) == 0 { 53 return 0, nil 54 } 55 port, err := strconv.ParseUint(rawPort, 10, 16) 56 if err != nil { 57 return 0, err 58 } 59 return int(port), nil 60 } 61 62 // Proto returns the protocol of a Port 63 func (p Port) Proto() string { 64 proto, _ := SplitProtoPort(string(p)) 65 return proto 66 } 67 68 // Port returns the port number of a Port 69 func (p Port) Port() string { 70 _, port := SplitProtoPort(string(p)) 71 return port 72 } 73 74 // Int returns the port number of a Port as an int 75 func (p Port) Int() int { 76 portStr := p.Port() 77 if len(portStr) == 0 { 78 return 0 79 } 80 81 // We don't need to check for an error because we're going to 82 // assume that any error would have been found, and reported, in NewPort() 83 port, _ := strconv.ParseUint(portStr, 10, 16) 84 return int(port) 85 } 86 87 // SplitProtoPort splits a port in the format of proto/port 88 func SplitProtoPort(rawPort string) (string, string) { 89 parts := strings.Split(rawPort, "/") 90 l := len(parts) 91 if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 { 92 return "", "" 93 } 94 if l == 1 { 95 return "tcp", rawPort 96 } 97 if len(parts[1]) == 0 { 98 return "tcp", parts[0] 99 } 100 return parts[1], parts[0] 101 } 102 103 func validateProto(proto string) bool { 104 for _, availableProto := range []string{"tcp", "udp"} { 105 if availableProto == proto { 106 return true 107 } 108 } 109 return false 110 } 111 112 // ParsePortSpecs receives port specs in the format of ip:public:private/proto and parses 113 // these in to the internal types 114 func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) { 115 var ( 116 exposedPorts = make(map[Port]struct{}, len(ports)) 117 bindings = make(map[Port][]PortBinding) 118 ) 119 120 for _, rawPort := range ports { 121 proto := "tcp" 122 123 if i := strings.LastIndex(rawPort, "/"); i != -1 { 124 proto = rawPort[i+1:] 125 rawPort = rawPort[:i] 126 } 127 if !strings.Contains(rawPort, ":") { 128 rawPort = fmt.Sprintf("::%s", rawPort) 129 } else if len(strings.Split(rawPort, ":")) == 2 { 130 rawPort = fmt.Sprintf(":%s", rawPort) 131 } 132 133 parts, err := parsers.PartParser(portSpecTemplate, rawPort) 134 if err != nil { 135 return nil, nil, err 136 } 137 138 var ( 139 containerPort = parts["containerPort"] 140 rawIP = parts["ip"] 141 hostPort = parts["hostPort"] 142 ) 143 144 if rawIP != "" && net.ParseIP(rawIP) == nil { 145 return nil, nil, fmt.Errorf("Invalid ip address: %s", rawIP) 146 } 147 if containerPort == "" { 148 return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort) 149 } 150 151 startPort, endPort, err := parsers.ParsePortRange(containerPort) 152 if err != nil { 153 return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort) 154 } 155 156 var startHostPort, endHostPort uint64 = 0, 0 157 if len(hostPort) > 0 { 158 startHostPort, endHostPort, err = parsers.ParsePortRange(hostPort) 159 if err != nil { 160 return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort) 161 } 162 } 163 164 if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) { 165 return nil, nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort) 166 } 167 168 if !validateProto(strings.ToLower(proto)) { 169 return nil, nil, fmt.Errorf("Invalid proto: %s", proto) 170 } 171 172 for i := uint64(0); i <= (endPort - startPort); i++ { 173 containerPort = strconv.FormatUint(startPort+i, 10) 174 if len(hostPort) > 0 { 175 hostPort = strconv.FormatUint(startHostPort+i, 10) 176 } 177 port, err := NewPort(strings.ToLower(proto), containerPort) 178 if err != nil { 179 return nil, nil, err 180 } 181 if _, exists := exposedPorts[port]; !exists { 182 exposedPorts[port] = struct{}{} 183 } 184 185 binding := PortBinding{ 186 HostIP: rawIP, 187 HostPort: hostPort, 188 } 189 bslice, exists := bindings[port] 190 if !exists { 191 bslice = []PortBinding{} 192 } 193 bindings[port] = append(bslice, binding) 194 } 195 } 196 return exposedPorts, bindings, nil 197 }