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