external_ip_mapper.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. // SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
  2. // SPDX-License-Identifier: MIT
  3. package ice
  4. import (
  5. "net"
  6. "strings"
  7. )
  8. func validateIPString(ipStr string) (net.IP, bool, error) {
  9. ip := net.ParseIP(ipStr)
  10. if ip == nil {
  11. return nil, false, ErrInvalidNAT1To1IPMapping
  12. }
  13. return ip, (ip.To4() != nil), nil
  14. }
  15. // ipMapping holds the mapping of local and external IP address for a particular IP family
  16. type ipMapping struct {
  17. ipSole net.IP // When non-nil, this is the sole external IP for one local IP assumed
  18. ipMap map[string]net.IP // Local-to-external IP mapping (k: local, v: external)
  19. valid bool // If not set any external IP, valid is false
  20. }
  21. func (m *ipMapping) setSoleIP(ip net.IP) error {
  22. if m.ipSole != nil || len(m.ipMap) > 0 {
  23. return ErrInvalidNAT1To1IPMapping
  24. }
  25. m.ipSole = ip
  26. m.valid = true
  27. return nil
  28. }
  29. func (m *ipMapping) addIPMapping(locIP, extIP net.IP) error {
  30. if m.ipSole != nil {
  31. return ErrInvalidNAT1To1IPMapping
  32. }
  33. locIPStr := locIP.String()
  34. // Check if dup of local IP
  35. if _, ok := m.ipMap[locIPStr]; ok {
  36. return ErrInvalidNAT1To1IPMapping
  37. }
  38. m.ipMap[locIPStr] = extIP
  39. m.valid = true
  40. return nil
  41. }
  42. func (m *ipMapping) findExternalIP(locIP net.IP) (net.IP, error) {
  43. if !m.valid {
  44. return locIP, nil
  45. }
  46. if m.ipSole != nil {
  47. return m.ipSole, nil
  48. }
  49. extIP, ok := m.ipMap[locIP.String()]
  50. if !ok {
  51. return nil, ErrExternalMappedIPNotFound
  52. }
  53. return extIP, nil
  54. }
  55. type externalIPMapper struct {
  56. ipv4Mapping ipMapping
  57. ipv6Mapping ipMapping
  58. candidateType CandidateType
  59. }
  60. func newExternalIPMapper(candidateType CandidateType, ips []string) (*externalIPMapper, error) { //nolint:gocognit
  61. if len(ips) == 0 {
  62. return nil, nil //nolint:nilnil
  63. }
  64. if candidateType == CandidateTypeUnspecified {
  65. candidateType = CandidateTypeHost // Defaults to host
  66. } else if candidateType != CandidateTypeHost && candidateType != CandidateTypeServerReflexive {
  67. return nil, ErrUnsupportedNAT1To1IPCandidateType
  68. }
  69. m := &externalIPMapper{
  70. ipv4Mapping: ipMapping{ipMap: map[string]net.IP{}},
  71. ipv6Mapping: ipMapping{ipMap: map[string]net.IP{}},
  72. candidateType: candidateType,
  73. }
  74. for _, extIPStr := range ips {
  75. ipPair := strings.Split(extIPStr, "/")
  76. if len(ipPair) == 0 || len(ipPair) > 2 {
  77. return nil, ErrInvalidNAT1To1IPMapping
  78. }
  79. extIP, isExtIPv4, err := validateIPString(ipPair[0])
  80. if err != nil {
  81. return nil, err
  82. }
  83. if len(ipPair) == 1 {
  84. if isExtIPv4 {
  85. if err := m.ipv4Mapping.setSoleIP(extIP); err != nil {
  86. return nil, err
  87. }
  88. } else {
  89. if err := m.ipv6Mapping.setSoleIP(extIP); err != nil {
  90. return nil, err
  91. }
  92. }
  93. } else {
  94. locIP, isLocIPv4, err := validateIPString(ipPair[1])
  95. if err != nil {
  96. return nil, err
  97. }
  98. if isExtIPv4 {
  99. if !isLocIPv4 {
  100. return nil, ErrInvalidNAT1To1IPMapping
  101. }
  102. if err := m.ipv4Mapping.addIPMapping(locIP, extIP); err != nil {
  103. return nil, err
  104. }
  105. } else {
  106. if isLocIPv4 {
  107. return nil, ErrInvalidNAT1To1IPMapping
  108. }
  109. if err := m.ipv6Mapping.addIPMapping(locIP, extIP); err != nil {
  110. return nil, err
  111. }
  112. }
  113. }
  114. }
  115. return m, nil
  116. }
  117. func (m *externalIPMapper) findExternalIP(localIPStr string) (net.IP, error) {
  118. locIP, isLocIPv4, err := validateIPString(localIPStr)
  119. if err != nil {
  120. return nil, err
  121. }
  122. if isLocIPv4 {
  123. return m.ipv4Mapping.findExternalIP(locIP)
  124. }
  125. return m.ipv6Mapping.findExternalIP(locIP)
  126. }