nat.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // Package nat is a convenience package for manipulation of strings describing network ports.
  2. package nat
  3. import (
  4. "fmt"
  5. "net"
  6. "strconv"
  7. "strings"
  8. )
  9. const (
  10. // portSpecTemplate is the expected format for port specifications
  11. portSpecTemplate = "ip:hostPort:containerPort"
  12. )
  13. // PortBinding represents a binding between a Host IP address and a Host Port
  14. type PortBinding struct {
  15. // HostIP is the host IP Address
  16. HostIP string `json:"HostIp"`
  17. // HostPort is the host port number
  18. HostPort string
  19. }
  20. // PortMap is a collection of PortBinding indexed by Port
  21. type PortMap map[Port][]PortBinding
  22. // PortSet is a collection of structs indexed by Port
  23. type PortSet map[Port]struct{}
  24. // Port is a string containing port number and protocol in the format "80/tcp"
  25. type Port string
  26. // NewPort creates a new instance of a Port given a protocol and port number or port range
  27. func NewPort(proto, port string) (Port, error) {
  28. // Check for parsing issues on "port" now so we can avoid having
  29. // to check it later on.
  30. portStartInt, portEndInt, err := ParsePortRangeToInt(port)
  31. if err != nil {
  32. return "", err
  33. }
  34. if portStartInt == portEndInt {
  35. return Port(fmt.Sprintf("%d/%s", portStartInt, proto)), nil
  36. }
  37. return Port(fmt.Sprintf("%d-%d/%s", portStartInt, portEndInt, proto)), nil
  38. }
  39. // ParsePort parses the port number string and returns an int
  40. func ParsePort(rawPort string) (int, error) {
  41. if len(rawPort) == 0 {
  42. return 0, nil
  43. }
  44. port, err := strconv.ParseUint(rawPort, 10, 16)
  45. if err != nil {
  46. return 0, err
  47. }
  48. return int(port), nil
  49. }
  50. // ParsePortRangeToInt parses the port range string and returns start/end ints
  51. func ParsePortRangeToInt(rawPort string) (int, int, error) {
  52. if len(rawPort) == 0 {
  53. return 0, 0, nil
  54. }
  55. start, end, err := ParsePortRange(rawPort)
  56. if err != nil {
  57. return 0, 0, err
  58. }
  59. return int(start), int(end), nil
  60. }
  61. // Proto returns the protocol of a Port
  62. func (p Port) Proto() string {
  63. proto, _ := SplitProtoPort(string(p))
  64. return proto
  65. }
  66. // Port returns the port number of a Port
  67. func (p Port) Port() string {
  68. _, port := SplitProtoPort(string(p))
  69. return port
  70. }
  71. // Int returns the port number of a Port as an int
  72. func (p Port) Int() int {
  73. portStr := p.Port()
  74. if len(portStr) == 0 {
  75. return 0
  76. }
  77. // We don't need to check for an error because we're going to
  78. // assume that any error would have been found, and reported, in NewPort()
  79. port, _ := strconv.ParseUint(portStr, 10, 16)
  80. return int(port)
  81. }
  82. // Range returns the start/end port numbers of a Port range as ints
  83. func (p Port) Range() (int, int, error) {
  84. return ParsePortRangeToInt(p.Port())
  85. }
  86. // SplitProtoPort splits a port in the format of proto/port
  87. func SplitProtoPort(rawPort string) (string, string) {
  88. parts := strings.Split(rawPort, "/")
  89. l := len(parts)
  90. if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 {
  91. return "", ""
  92. }
  93. if l == 1 {
  94. return "tcp", rawPort
  95. }
  96. if len(parts[1]) == 0 {
  97. return "tcp", parts[0]
  98. }
  99. return parts[1], parts[0]
  100. }
  101. func validateProto(proto string) bool {
  102. for _, availableProto := range []string{"tcp", "udp"} {
  103. if availableProto == proto {
  104. return true
  105. }
  106. }
  107. return false
  108. }
  109. // ParsePortSpecs receives port specs in the format of ip:public:private/proto and parses
  110. // these in to the internal types
  111. func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding, error) {
  112. var (
  113. exposedPorts = make(map[Port]struct{}, len(ports))
  114. bindings = make(map[Port][]PortBinding)
  115. )
  116. for _, rawPort := range ports {
  117. proto := "tcp"
  118. if i := strings.LastIndex(rawPort, "/"); i != -1 {
  119. proto = rawPort[i+1:]
  120. rawPort = rawPort[:i]
  121. }
  122. if !strings.Contains(rawPort, ":") {
  123. rawPort = fmt.Sprintf("::%s", rawPort)
  124. } else if len(strings.Split(rawPort, ":")) == 2 {
  125. rawPort = fmt.Sprintf(":%s", rawPort)
  126. }
  127. parts, err := PartParser(portSpecTemplate, rawPort)
  128. if err != nil {
  129. return nil, nil, err
  130. }
  131. var (
  132. containerPort = parts["containerPort"]
  133. rawIP = parts["ip"]
  134. hostPort = parts["hostPort"]
  135. )
  136. if rawIP != "" && net.ParseIP(rawIP) == nil {
  137. return nil, nil, fmt.Errorf("Invalid ip address: %s", rawIP)
  138. }
  139. if containerPort == "" {
  140. return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
  141. }
  142. startPort, endPort, err := ParsePortRange(containerPort)
  143. if err != nil {
  144. return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
  145. }
  146. var startHostPort, endHostPort uint64 = 0, 0
  147. if len(hostPort) > 0 {
  148. startHostPort, endHostPort, err = ParsePortRange(hostPort)
  149. if err != nil {
  150. return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
  151. }
  152. }
  153. if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) {
  154. // Allow host port range iff containerPort is not a range.
  155. // In this case, use the host port range as the dynamic
  156. // host port range to allocate into.
  157. if endPort != startPort {
  158. return nil, nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
  159. }
  160. }
  161. if !validateProto(strings.ToLower(proto)) {
  162. return nil, nil, fmt.Errorf("Invalid proto: %s", proto)
  163. }
  164. for i := uint64(0); i <= (endPort - startPort); i++ {
  165. containerPort = strconv.FormatUint(startPort+i, 10)
  166. if len(hostPort) > 0 {
  167. hostPort = strconv.FormatUint(startHostPort+i, 10)
  168. }
  169. // Set hostPort to a range only if there is a single container port
  170. // and a dynamic host port.
  171. if startPort == endPort && startHostPort != endHostPort {
  172. hostPort = fmt.Sprintf("%s-%s", hostPort, strconv.FormatUint(endHostPort, 10))
  173. }
  174. port, err := NewPort(strings.ToLower(proto), containerPort)
  175. if err != nil {
  176. return nil, nil, err
  177. }
  178. if _, exists := exposedPorts[port]; !exists {
  179. exposedPorts[port] = struct{}{}
  180. }
  181. binding := PortBinding{
  182. HostIP: rawIP,
  183. HostPort: hostPort,
  184. }
  185. bslice, exists := bindings[port]
  186. if !exists {
  187. bslice = []PortBinding{}
  188. }
  189. bindings[port] = append(bslice, binding)
  190. }
  191. }
  192. return exposedPorts, bindings, nil
  193. }