123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- // Copyright (C) 2015 The Syncthing Authors.
- //
- // This Source Code Form is subject to the terms of the Mozilla Public
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
- // You can obtain one at https://mozilla.org/MPL/2.0/.
- package nat
- import (
- "fmt"
- "log/slog"
- "net"
- "strconv"
- "sync"
- "time"
- )
- type MappingChangeSubscriber func()
- type Mapping struct {
- protocol Protocol
- ipVersion IPVersion
- address Address
- extAddresses map[string][]Address // NAT ID -> Address
- expires time.Time
- subscribers []MappingChangeSubscriber
- mut sync.RWMutex
- }
- func (m *Mapping) setAddressLocked(id string, addresses []Address) {
- slog.Info("New external port opened", "protocol", m.protocol, "external", addresses, "local", m.address, "gateway", id)
- m.extAddresses[id] = addresses
- }
- func (m *Mapping) removeAddressLocked(id string) {
- addresses, ok := m.extAddresses[id]
- if ok {
- slog.Info("Removing external open port", "protocol", m.protocol, "external", addresses, "gateway", id)
- delete(m.extAddresses, id)
- }
- }
- func (m *Mapping) clearAddresses() {
- m.mut.Lock()
- change := len(m.extAddresses) > 0
- for id, addr := range m.extAddresses {
- l.Debugf("Clearing mapping %s: ID: %s Address: %s", m, id, addr)
- delete(m.extAddresses, id)
- }
- m.expires = time.Time{}
- m.mut.Unlock()
- if change {
- m.notify()
- }
- }
- func (m *Mapping) notify() {
- m.mut.RLock()
- for _, subscriber := range m.subscribers {
- subscriber()
- }
- m.mut.RUnlock()
- }
- func (m *Mapping) Protocol() Protocol {
- return m.protocol
- }
- func (m *Mapping) Address() Address {
- return m.address
- }
- func (m *Mapping) ExternalAddresses() []Address {
- m.mut.RLock()
- addrs := make([]Address, 0, len(m.extAddresses))
- for _, addr := range m.extAddresses {
- addrs = append(addrs, addr...)
- }
- m.mut.RUnlock()
- return addrs
- }
- func (m *Mapping) OnChanged(subscribed MappingChangeSubscriber) {
- m.mut.Lock()
- m.subscribers = append(m.subscribers, subscribed)
- m.mut.Unlock()
- }
- func (m *Mapping) String() string {
- return fmt.Sprintf("%s/%s", m.address, m.protocol)
- }
- func (m *Mapping) GoString() string {
- return m.String()
- }
- // Checks if the mappings local IP address matches the IP address of the gateway
- // For example, if we are explicitly listening on 192.168.0.12, there is no
- // point trying to acquire a mapping on a gateway to which the local IP is
- // 10.0.0.1. Fallback to true if any of the IPs is not there.
- func (m *Mapping) validGateway(ip net.IP) bool {
- if m.address.IP == nil || ip == nil || m.address.IP.IsUnspecified() || ip.IsUnspecified() {
- return true
- }
- return m.address.IP.Equal(ip)
- }
- // Address is essentially net.TCPAddr yet is more general, and has a few helper
- // methods which reduce boilerplate code.
- type Address struct {
- IP net.IP
- Port int
- }
- func (a Address) Equal(b Address) bool {
- return a.Port == b.Port && a.IP.Equal(b.IP)
- }
- func (a Address) String() string {
- var ipStr string
- if a.IP == nil {
- ipStr = net.IPv4zero.String()
- } else {
- ipStr = a.IP.String()
- }
- return net.JoinHostPort(ipStr, strconv.Itoa(a.Port))
- }
- func (a Address) GoString() string {
- return a.String()
- }
|