github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/nat/behavior/service.go (about) 1 /* 2 * Copyright (C) 2021 The "MysteriumNetwork/node" Authors. 3 * 4 * This program is free software: you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation, either version 3 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18 package behavior 19 20 import ( 21 "context" 22 "errors" 23 "time" 24 25 "github.com/mysteriumnetwork/node/core/connection/connectionstate" 26 "github.com/mysteriumnetwork/node/eventbus" 27 "github.com/mysteriumnetwork/node/nat" 28 ) 29 30 // Only servers compatible with RFC 5780 are usable! 31 var compatibleSTUNServers = []string{ 32 "stun.mysterium.network:3478", 33 "stun.stunprotocol.org:3478", 34 "stun.sip.us:3478", 35 } 36 37 const ( 38 // AppTopicNATTypeDetected represents NAT type detection topic. 39 AppTopicNATTypeDetected = "NAT-type-detected" 40 41 concurrentRequestTimeout = 1 * time.Second 42 ) 43 44 // ErrInappropriateState error is returned by gatedNATProber when connection 45 // is active 46 var ErrInappropriateState = errors.New("NAT probing is impossible at this connection state") 47 48 // NATProber is an abstaction over instances capable probing NAT and 49 // returning either it's type or error. 50 type NATProber interface { 51 Probe(context.Context) (nat.NATType, error) 52 } 53 54 // ConnectionStatusProvider is a subset of connection.Manager methods 55 // to provide gatedNATProber with current connection status 56 type ConnectionStatusProvider interface { 57 Status(int) connectionstate.Status 58 } 59 60 // NewNATProber constructs some suitable NATProber without any implementation 61 // guarantees. 62 func NewNATProber(connStatusProvider ConnectionStatusProvider, eventbus eventbus.Publisher) NATProber { 63 var prober NATProber 64 prober = newConcurrentNATProber(compatibleSTUNServers, concurrentRequestTimeout) 65 prober = newGatedNATProber(connStatusProvider, eventbus, prober) 66 return prober 67 } 68 69 // Probes NAT status with parallel tests against multiple STUN servers 70 type concurrentNATProber struct { 71 servers []string 72 timeout time.Duration 73 } 74 75 func newConcurrentNATProber(servers []string, timeout time.Duration) *concurrentNATProber { 76 return &concurrentNATProber{ 77 servers: servers, 78 timeout: timeout, 79 } 80 } 81 82 func (p *concurrentNATProber) Probe(ctx context.Context) (nat.NATType, error) { 83 return RacingDiscoverNATBehavior(ctx, p.servers, p.timeout) 84 } 85 86 // Gates calls to other NATProber, allowing them only when node is not connected 87 type gatedNATProber struct { 88 next NATProber 89 connStatusProvider ConnectionStatusProvider 90 eventbus eventbus.Publisher 91 } 92 93 func newGatedNATProber(connStatusProvider ConnectionStatusProvider, eventbus eventbus.Publisher, next NATProber) *gatedNATProber { 94 return &gatedNATProber{ 95 next: next, 96 connStatusProvider: connStatusProvider, 97 eventbus: eventbus, 98 } 99 } 100 101 func (p *gatedNATProber) Probe(ctx context.Context) (nat.NATType, error) { 102 if p.connStatusProvider.Status(0).State != connectionstate.NotConnected { 103 return "", ErrInappropriateState 104 } 105 106 s, err := p.next.Probe(ctx) 107 if err != nil { 108 return "", err 109 } 110 111 p.eventbus.Publish(AppTopicNATTypeDetected, s) 112 return s, nil 113 }