1package nat
2
3import (
4	"fmt"
5	"net"
6	"sync"
7	"time"
8
9	"github.com/jbenet/goprocess"
10)
11
12// Mapping represents a port mapping in a NAT.
13type Mapping interface {
14	// NAT returns the NAT object this Mapping belongs to.
15	NAT() *NAT
16
17	// Protocol returns the protocol of this port mapping. This is either
18	// "tcp" or "udp" as no other protocols are likely to be NAT-supported.
19	Protocol() string
20
21	// InternalPort returns the internal device port. Mapping will continue to
22	// try to map InternalPort() to an external facing port.
23	InternalPort() int
24
25	// ExternalPort returns the external facing port. If the mapping is not
26	// established, port will be 0
27	ExternalPort() int
28
29	// ExternalAddr returns the external facing address. If the mapping is not
30	// established, addr will be nil, and and ErrNoMapping will be returned.
31	ExternalAddr() (addr net.Addr, err error)
32
33	// Close closes the port mapping
34	Close() error
35}
36
37// keeps republishing
38type mapping struct {
39	sync.Mutex // guards all fields
40
41	nat       *NAT
42	proto     string
43	intport   int
44	extport   int
45	permanent bool
46	proc      goprocess.Process
47
48	cached    net.IP
49	cacheTime time.Time
50	cacheLk   sync.Mutex
51}
52
53func (m *mapping) NAT() *NAT {
54	m.Lock()
55	defer m.Unlock()
56	return m.nat
57}
58
59func (m *mapping) Protocol() string {
60	m.Lock()
61	defer m.Unlock()
62	return m.proto
63}
64
65func (m *mapping) InternalPort() int {
66	m.Lock()
67	defer m.Unlock()
68	return m.intport
69}
70
71func (m *mapping) ExternalPort() int {
72	m.Lock()
73	defer m.Unlock()
74	return m.extport
75}
76
77func (m *mapping) setExternalPort(p int) {
78	m.Lock()
79	defer m.Unlock()
80	m.extport = p
81}
82
83func (m *mapping) ExternalAddr() (net.Addr, error) {
84	m.cacheLk.Lock()
85	defer m.cacheLk.Unlock()
86	oport := m.ExternalPort()
87	if oport == 0 {
88		// dont even try right now.
89		return nil, ErrNoMapping
90	}
91
92	if time.Since(m.cacheTime) >= CacheTime {
93		m.nat.natmu.Lock()
94		cval, err := m.nat.nat.GetExternalAddress()
95		m.nat.natmu.Unlock()
96
97		if err != nil {
98			return nil, err
99		}
100
101		m.cached = cval
102		m.cacheTime = time.Now()
103	}
104	switch m.Protocol() {
105	case "tcp":
106		return &net.TCPAddr{
107			IP:   m.cached,
108			Port: oport,
109		}, nil
110	case "udp":
111		return &net.UDPAddr{
112			IP:   m.cached,
113			Port: oport,
114		}, nil
115	default:
116		panic(fmt.Sprintf("invalid protocol %q", m.Protocol()))
117	}
118}
119
120func (m *mapping) Close() error {
121	return m.proc.Close()
122}
123