144 lines
2.8 KiB
Go
144 lines
2.8 KiB
Go
|
package nat
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"github.com/jbenet/goprocess"
|
||
|
ma "github.com/multiformats/go-multiaddr"
|
||
|
manet "github.com/multiformats/go-multiaddr-net"
|
||
|
)
|
||
|
|
||
|
// 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()
|
||
|
}
|