-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #95 from libp2p/feat/nat-permanent-retry
Retry NAT punching without duration on mapping failure
- Loading branch information
Showing
3 changed files
with
207 additions
and
174 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
package nat | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
"time" | ||
|
||
ma "github.com/jbenet/go-multiaddr" | ||
manet "github.com/jbenet/go-multiaddr-net" | ||
"github.com/jbenet/goprocess" | ||
) | ||
|
||
// Mapping represents a port mapping in a NAT. | ||
type Mapping interface { | ||
// NAT returns the NAT object this Mapping belongs to. | ||
NAT() *NAT | ||
|
||
// Protocol returns the protocol of this port mapping. This is either | ||
// "tcp" or "udp" as no other protocols are likely to be NAT-supported. | ||
Protocol() string | ||
|
||
// InternalPort returns the internal device port. Mapping will continue to | ||
// try to map InternalPort() to an external facing port. | ||
InternalPort() int | ||
|
||
// ExternalPort returns the external facing port. If the mapping is not | ||
// established, port will be 0 | ||
ExternalPort() int | ||
|
||
// InternalAddr returns the internal address. | ||
InternalAddr() ma.Multiaddr | ||
|
||
// ExternalAddr returns the external facing address. If the mapping is not | ||
// established, addr will be nil, and and ErrNoMapping will be returned. | ||
ExternalAddr() (addr ma.Multiaddr, err error) | ||
|
||
// Close closes the port mapping | ||
Close() error | ||
} | ||
|
||
// keeps republishing | ||
type mapping struct { | ||
sync.Mutex // guards all fields | ||
|
||
nat *NAT | ||
proto string | ||
intport int | ||
extport int | ||
permanent bool | ||
intaddr ma.Multiaddr | ||
proc goprocess.Process | ||
|
||
comment string | ||
|
||
cached ma.Multiaddr | ||
cacheTime time.Time | ||
cacheLk sync.Mutex | ||
} | ||
|
||
func (m *mapping) NAT() *NAT { | ||
m.Lock() | ||
defer m.Unlock() | ||
return m.nat | ||
} | ||
|
||
func (m *mapping) Protocol() string { | ||
m.Lock() | ||
defer m.Unlock() | ||
return m.proto | ||
} | ||
|
||
func (m *mapping) InternalPort() int { | ||
m.Lock() | ||
defer m.Unlock() | ||
return m.intport | ||
} | ||
|
||
func (m *mapping) ExternalPort() int { | ||
m.Lock() | ||
defer m.Unlock() | ||
return m.extport | ||
} | ||
|
||
func (m *mapping) setExternalPort(p int) { | ||
m.Lock() | ||
defer m.Unlock() | ||
m.extport = p | ||
} | ||
|
||
func (m *mapping) InternalAddr() ma.Multiaddr { | ||
m.Lock() | ||
defer m.Unlock() | ||
return m.intaddr | ||
} | ||
|
||
func (m *mapping) ExternalAddr() (ma.Multiaddr, error) { | ||
m.cacheLk.Lock() | ||
ctime := m.cacheTime | ||
cval := m.cached | ||
m.cacheLk.Unlock() | ||
if time.Since(ctime) < CacheTime { | ||
return cval, nil | ||
} | ||
|
||
if m.ExternalPort() == 0 { // dont even try right now. | ||
return nil, ErrNoMapping | ||
} | ||
|
||
m.nat.natmu.Lock() | ||
ip, err := m.nat.nat.GetExternalAddress() | ||
m.nat.natmu.Unlock() | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
ipmaddr, err := manet.FromIP(ip) | ||
if err != nil { | ||
return nil, fmt.Errorf("error parsing ip") | ||
} | ||
|
||
// call m.ExternalPort again, as mapping may have changed under our feet. (tocttou) | ||
extport := m.ExternalPort() | ||
if extport == 0 { | ||
return nil, ErrNoMapping | ||
} | ||
|
||
tcp, err := ma.NewMultiaddr(fmt.Sprintf("/%s/%d", m.Protocol(), extport)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
maddr2 := ipmaddr.Encapsulate(tcp) | ||
|
||
m.cacheLk.Lock() | ||
m.cached = maddr2 | ||
m.cacheTime = time.Now() | ||
m.cacheLk.Unlock() | ||
return maddr2, nil | ||
} | ||
|
||
func (m *mapping) Close() error { | ||
return m.proc.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.