github.com/cwandrews/docker@v1.7.0/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 = "ip:hostPort:containerPort" 17 PortSpecTemplateFormat = "ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort" 18 ) 19 20 type PortBinding struct { 21 HostIp string 22 HostPort string 23 } 24 25 type PortMap map[Port][]PortBinding 26 27 type PortSet map[Port]struct{} 28 29 // 80/tcp 30 type Port string 31 32 func NewPort(proto, port string) Port { 33 return Port(fmt.Sprintf("%s/%s", port, proto)) 34 } 35 36 func ParsePort(rawPort string) (int, error) { 37 if len(rawPort) == 0 { 38 return 0, nil 39 } 40 port, err := strconv.ParseUint(rawPort, 10, 16) 41 if err != nil { 42 return 0, err 43 } 44 return int(port), nil 45 } 46 47 func (p Port) Proto() string { 48 proto, _ := SplitProtoPort(string(p)) 49 return proto 50 } 51 52 func (p Port) Port() string { 53 _, port := SplitProtoPort(string(p)) 54 return port 55 } 56 57 func (p Port) Int() int { 58 port, err := ParsePort(p.Port()) 59 if err != nil { 60 panic(err) 61 } 62 return port 63 } 64 65 // Splits a port in the format of proto/port 66 func SplitProtoPort(rawPort string) (string, string) { 67 parts := strings.Split(rawPort, "/") 68 l := len(parts) 69 if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 { 70 return "", "" 71 } 72 if l == 1 { 73 return "tcp", rawPort 74 } 75 if len(parts[1]) == 0 { 76 return "tcp", parts[0] 77 } 78 return parts[1], parts[0] 79 } 80 81 func validateProto(proto string) bool { 82 for _, availableProto := range []string{"tcp", "udp"} { 83 if availableProto == proto { 84 return true 85 } 86 } 87 return false 88 } 89 90 // We will receive port specs in the format of ip:public:private/proto and these need to be 91 // parsed in the internal types 92 func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) { 93 var ( 94 exposedPorts = make(map[Port]struct{}, len(ports)) 95 bindings = make(map[Port][]PortBinding) 96 ) 97 98 for _, rawPort := range ports { 99 proto := "tcp" 100 101 if i := strings.LastIndex(rawPort, "/"); i != -1 { 102 proto = rawPort[i+1:] 103 rawPort = rawPort[:i] 104 } 105 if !strings.Contains(rawPort, ":") { 106 rawPort = fmt.Sprintf("::%s", rawPort) 107 } else if len(strings.Split(rawPort, ":")) == 2 { 108 rawPort = fmt.Sprintf(":%s", rawPort) 109 } 110 111 parts, err := parsers.PartParser(PortSpecTemplate, rawPort) 112 if err != nil { 113 return nil, nil, err 114 } 115 116 var ( 117 containerPort = parts["containerPort"] 118 rawIp = parts["ip"] 119 hostPort = parts["hostPort"] 120 ) 121 122 if rawIp != "" && net.ParseIP(rawIp) == nil { 123 return nil, nil, fmt.Errorf("Invalid ip address: %s", rawIp) 124 } 125 if containerPort == "" { 126 return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort) 127 } 128 129 startPort, endPort, err := parsers.ParsePortRange(containerPort) 130 if err != nil { 131 return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort) 132 } 133 134 var startHostPort, endHostPort uint64 = 0, 0 135 if len(hostPort) > 0 { 136 startHostPort, endHostPort, err = parsers.ParsePortRange(hostPort) 137 if err != nil { 138 return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort) 139 } 140 } 141 142 if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) { 143 return nil, nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort) 144 } 145 146 if !validateProto(strings.ToLower(proto)) { 147 return nil, nil, fmt.Errorf("Invalid proto: %s", proto) 148 } 149 150 for i := uint64(0); i <= (endPort - startPort); i++ { 151 containerPort = strconv.FormatUint(startPort+i, 10) 152 if len(hostPort) > 0 { 153 hostPort = strconv.FormatUint(startHostPort+i, 10) 154 } 155 port := NewPort(strings.ToLower(proto), containerPort) 156 if _, exists := exposedPorts[port]; !exists { 157 exposedPorts[port] = struct{}{} 158 } 159 160 binding := PortBinding{ 161 HostIp: rawIp, 162 HostPort: hostPort, 163 } 164 bslice, exists := bindings[port] 165 if !exists { 166 bslice = []PortBinding{} 167 } 168 bindings[port] = append(bslice, binding) 169 } 170 } 171 return exposedPorts, bindings, nil 172 }