github.com/jogo/docker@v1.7.0-rc1/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  }