github.com/lmars/docker@v1.6.0-rc2/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  }