123 lines
2.3 KiB
Go
123 lines
2.3 KiB
Go
package nat
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
"time"
|
|
|
|
"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
|
|
|
|
// ExternalAddr returns the external facing address. If the mapping is not
|
|
// established, addr will be nil, and and ErrNoMapping will be returned.
|
|
ExternalAddr() (addr net.Addr, 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
|
|
proc goprocess.Process
|
|
|
|
cached net.IP
|
|
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) ExternalAddr() (net.Addr, error) {
|
|
m.cacheLk.Lock()
|
|
defer m.cacheLk.Unlock()
|
|
oport := m.ExternalPort()
|
|
if oport == 0 {
|
|
// dont even try right now.
|
|
return nil, ErrNoMapping
|
|
}
|
|
|
|
if time.Since(m.cacheTime) >= CacheTime {
|
|
m.nat.natmu.Lock()
|
|
cval, err := m.nat.nat.GetExternalAddress()
|
|
m.nat.natmu.Unlock()
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
m.cached = cval
|
|
m.cacheTime = time.Now()
|
|
}
|
|
switch m.Protocol() {
|
|
case "tcp":
|
|
return &net.TCPAddr{
|
|
IP: m.cached,
|
|
Port: oport,
|
|
}, nil
|
|
case "udp":
|
|
return &net.UDPAddr{
|
|
IP: m.cached,
|
|
Port: oport,
|
|
}, nil
|
|
default:
|
|
panic(fmt.Sprintf("invalid protocol %q", m.Protocol()))
|
|
}
|
|
}
|
|
|
|
func (m *mapping) Close() error {
|
|
return m.proc.Close()
|
|
}
|