discovery.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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"
  20. "github.com/fatedier/frp/pkg/msg"
  21. )
  22. var responseTimeout = 3 * time.Second
  23. type Message struct {
  24. Body []byte
  25. Addr string
  26. }
  27. func Discover(serverAddress string, stunServers []string, key []byte) ([]string, error) {
  28. // create a discoverConn and get response from messageChan
  29. discoverConn, err := listen()
  30. if err != nil {
  31. return nil, err
  32. }
  33. defer discoverConn.Close()
  34. go discoverConn.readLoop()
  35. addresses := make([]string, 0, len(stunServers)+1)
  36. if serverAddress != "" {
  37. // get external address from frp server
  38. externalAddr, err := discoverConn.discoverFromServer(serverAddress, key)
  39. if err != nil {
  40. return nil, err
  41. }
  42. addresses = append(addresses, externalAddr)
  43. }
  44. for _, addr := range stunServers {
  45. // get external address from stun server
  46. externalAddrs, err := discoverConn.discoverFromStunServer(addr)
  47. if err != nil {
  48. return nil, err
  49. }
  50. addresses = append(addresses, externalAddrs...)
  51. }
  52. return addresses, nil
  53. }
  54. type stunResponse struct {
  55. externalAddr string
  56. otherAddr string
  57. }
  58. type discoverConn struct {
  59. conn *net.UDPConn
  60. localAddr net.Addr
  61. messageChan chan *Message
  62. }
  63. func listen() (*discoverConn, error) {
  64. conn, err := net.ListenUDP("udp4", nil)
  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) discoverFromServer(serverAddress string, key []byte) (string, error) {
  140. addr, err := net.ResolveUDPAddr("udp4", serverAddress)
  141. if err != nil {
  142. return "", err
  143. }
  144. m := &msg.NatHoleBinding{
  145. TransactionID: NewTransactionID(),
  146. }
  147. buf, err := EncodeMessage(m, key)
  148. if err != nil {
  149. return "", err
  150. }
  151. if _, err := c.conn.WriteTo(buf, addr); err != nil {
  152. return "", err
  153. }
  154. var respMsg msg.NatHoleBindingResp
  155. select {
  156. case rawMsg := <-c.messageChan:
  157. if err := DecodeMessageInto(rawMsg.Body, key, &respMsg); err != nil {
  158. return "", err
  159. }
  160. case <-time.After(responseTimeout):
  161. return "", fmt.Errorf("wait response from frp server timeout")
  162. }
  163. if respMsg.TransactionID == "" {
  164. return "", fmt.Errorf("error format: no transaction id found")
  165. }
  166. if respMsg.Error != "" {
  167. return "", fmt.Errorf("get externalAddr from frp server error: %s", respMsg.Error)
  168. }
  169. return respMsg.Address, nil
  170. }
  171. func (c *discoverConn) discoverFromStunServer(addr string) ([]string, error) {
  172. resp, err := c.doSTUNRequest(addr)
  173. if err != nil {
  174. return nil, err
  175. }
  176. if resp.externalAddr == "" {
  177. return nil, fmt.Errorf("no external address found")
  178. }
  179. externalAddrs := make([]string, 0, 2)
  180. externalAddrs = append(externalAddrs, resp.externalAddr)
  181. if resp.otherAddr == "" {
  182. return externalAddrs, nil
  183. }
  184. // find external address from changed address
  185. resp, err = c.doSTUNRequest(resp.otherAddr)
  186. if err != nil {
  187. return nil, err
  188. }
  189. if resp.externalAddr != "" {
  190. externalAddrs = append(externalAddrs, resp.externalAddr)
  191. }
  192. return externalAddrs, nil
  193. }