discovery.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // Copyright 2023 The frp Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package nathole
  15. import (
  16. "fmt"
  17. "net"
  18. "time"
  19. "github.com/pion/stun/v2"
  20. )
  21. var responseTimeout = 3 * time.Second
  22. type Message struct {
  23. Body []byte
  24. Addr string
  25. }
  26. // If the localAddr is empty, it will listen on a random port.
  27. func Discover(stunServers []string, localAddr string) ([]string, net.Addr, error) {
  28. // create a discoverConn and get response from messageChan
  29. discoverConn, err := listen(localAddr)
  30. if err != nil {
  31. return nil, nil, err
  32. }
  33. defer discoverConn.Close()
  34. go discoverConn.readLoop()
  35. addresses := make([]string, 0, len(stunServers))
  36. for _, addr := range stunServers {
  37. // get external address from stun server
  38. externalAddrs, err := discoverConn.discoverFromStunServer(addr)
  39. if err != nil {
  40. return nil, nil, err
  41. }
  42. addresses = append(addresses, externalAddrs...)
  43. }
  44. return addresses, discoverConn.localAddr, nil
  45. }
  46. type stunResponse struct {
  47. externalAddr string
  48. otherAddr string
  49. }
  50. type discoverConn struct {
  51. conn *net.UDPConn
  52. localAddr net.Addr
  53. messageChan chan *Message
  54. }
  55. func listen(localAddr string) (*discoverConn, error) {
  56. var local *net.UDPAddr
  57. if localAddr != "" {
  58. addr, err := net.ResolveUDPAddr("udp4", localAddr)
  59. if err != nil {
  60. return nil, err
  61. }
  62. local = addr
  63. }
  64. conn, err := net.ListenUDP("udp4", local)
  65. if err != nil {
  66. return nil, err
  67. }
  68. return &discoverConn{
  69. conn: conn,
  70. localAddr: conn.LocalAddr(),
  71. messageChan: make(chan *Message, 10),
  72. }, nil
  73. }
  74. func (c *discoverConn) Close() error {
  75. if c.messageChan != nil {
  76. close(c.messageChan)
  77. c.messageChan = nil
  78. }
  79. return c.conn.Close()
  80. }
  81. func (c *discoverConn) readLoop() {
  82. for {
  83. buf := make([]byte, 1024)
  84. n, addr, err := c.conn.ReadFromUDP(buf)
  85. if err != nil {
  86. return
  87. }
  88. buf = buf[:n]
  89. c.messageChan <- &Message{
  90. Body: buf,
  91. Addr: addr.String(),
  92. }
  93. }
  94. }
  95. func (c *discoverConn) doSTUNRequest(addr string) (*stunResponse, error) {
  96. serverAddr, err := net.ResolveUDPAddr("udp4", addr)
  97. if err != nil {
  98. return nil, err
  99. }
  100. request, err := stun.Build(stun.TransactionID, stun.BindingRequest)
  101. if err != nil {
  102. return nil, err
  103. }
  104. if err = request.NewTransactionID(); err != nil {
  105. return nil, err
  106. }
  107. if _, err := c.conn.WriteTo(request.Raw, serverAddr); err != nil {
  108. return nil, err
  109. }
  110. var m stun.Message
  111. select {
  112. case msg := <-c.messageChan:
  113. m.Raw = msg.Body
  114. if err := m.Decode(); err != nil {
  115. return nil, err
  116. }
  117. case <-time.After(responseTimeout):
  118. return nil, fmt.Errorf("wait response from stun server timeout")
  119. }
  120. xorAddrGetter := &stun.XORMappedAddress{}
  121. mappedAddrGetter := &stun.MappedAddress{}
  122. changedAddrGetter := ChangedAddress{}
  123. otherAddrGetter := &stun.OtherAddress{}
  124. resp := &stunResponse{}
  125. if err := mappedAddrGetter.GetFrom(&m); err == nil {
  126. resp.externalAddr = mappedAddrGetter.String()
  127. }
  128. if err := xorAddrGetter.GetFrom(&m); err == nil {
  129. resp.externalAddr = xorAddrGetter.String()
  130. }
  131. if err := changedAddrGetter.GetFrom(&m); err == nil {
  132. resp.otherAddr = changedAddrGetter.String()
  133. }
  134. if err := otherAddrGetter.GetFrom(&m); err == nil {
  135. resp.otherAddr = otherAddrGetter.String()
  136. }
  137. return resp, nil
  138. }
  139. func (c *discoverConn) discoverFromStunServer(addr string) ([]string, error) {
  140. resp, err := c.doSTUNRequest(addr)
  141. if err != nil {
  142. return nil, err
  143. }
  144. if resp.externalAddr == "" {
  145. return nil, fmt.Errorf("no external address found")
  146. }
  147. externalAddrs := make([]string, 0, 2)
  148. externalAddrs = append(externalAddrs, resp.externalAddr)
  149. if resp.otherAddr == "" {
  150. return externalAddrs, nil
  151. }
  152. // find external address from changed address
  153. resp, err = c.doSTUNRequest(resp.otherAddr)
  154. if err != nil {
  155. return nil, err
  156. }
  157. if resp.externalAddr != "" {
  158. externalAddrs = append(externalAddrs, resp.externalAddr)
  159. }
  160. return externalAddrs, nil
  161. }