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  }