gost_software/vendor/github.com/isofew/go-stun/stun/nat.go
2018-05-17 13:14:45 +01:00

173 lines
3.7 KiB
Go

package stun
import (
"errors"
"net"
)
const (
EndpointIndependent = "endpoint-independent"
AddressDependent = "address-dependent"
AddressPortDependent = "address-port-dependent"
)
type Detector struct {
*Conn
}
func NewDetector(c *Conn) *Detector {
d := &Detector{c}
d.agent.Handler = &Server{agent: c.agent}
return d
}
func (d *Detector) Hairpinning() error {
mapped, err := d.Discover()
if err != nil {
return err
}
conn, err := net.Dial(d.Network(), mapped.String())
if err != nil {
return err
}
// not using stop channel
c := NewConn(conn, d.agent.config, make(chan struct{}))
defer c.Close()
_, err = c.Discover()
return err
}
func (d *Detector) DiscoverChange(change uint64) error {
req := &Message{Type: MethodBinding, Attributes: []Attr{Int(AttrChangeRequest, change)}}
_, from, err := d.RequestTransport(req, d)
if err != nil {
return err
}
ip, port := SockAddr(d.RemoteAddr())
chip, chport := SockAddr(from.RemoteAddr())
if change&ChangeIP != 0 {
if ip.Equal(chip) {
return errors.New("stun: bad response, ip address is not changed")
}
} else if change&ChangePort != 0 {
if port == chport {
return errors.New("stun: bad response, port is not changed")
}
}
return nil
}
func (d *Detector) Filtering() (string, error) {
n := d.Network()
if n != "udp" {
return "", errors.New("stun: filtering test is not applicable to " + n)
}
_, err := d.Request(&Message{Type: MethodBinding})
if err != nil {
return "", err
}
err = d.DiscoverChange(ChangeIP | ChangePort)
switch err {
case nil:
return EndpointIndependent, nil
case errTimeout:
err = d.DiscoverChange(ChangePort)
switch err {
case nil:
return AddressDependent, nil
case errTimeout:
return AddressPortDependent, nil
}
}
return "", err
}
func (d *Detector) DiscoverOther(addr net.Addr) (net.Addr, error) {
n := addr.Network()
conn, err := net.Dial(n, addr.String())
if err != nil {
return nil, err
}
defer conn.Close()
// not using stop channel
go d.agent.ServeConn(conn, make(chan struct{}))
res, _, err := d.RequestTransport(&Message{Type: MethodBinding}, conn)
if err != nil {
return nil, err
}
mapped := res.GetAddr(n, AttrXorMappedAddress, AttrMappedAddress)
if mapped != nil {
return mapped, nil
}
return nil, errors.New("stun: bad response, no mapped address")
}
func (d *Detector) Mapping() (string, error) {
n := d.Network()
msg, err := d.Request(&Message{Type: MethodBinding})
if err != nil {
return "", err
}
mapped, other := msg.GetAddr(n, AttrXorMappedAddress), msg.GetAddr(n, AttrOtherAddress)
if mapped == nil {
return "", errors.New("stun: bad response, no mapped address")
}
if other == nil {
return "", errors.New("stun: bad response, no other address")
}
ip, _ := SockAddr(mapped)
if ip.IsLoopback() {
return EndpointIndependent, nil
}
for _, it := range local {
if it.IP.Equal(ip) {
return EndpointIndependent, nil
}
}
ip, _ = SockAddr(other)
_, port := SockAddr(d.RemoteAddr())
a, err := d.DiscoverOther(NewAddr(n, ip, port))
if err != nil {
return "", err
}
if sameAddr(a, mapped) {
return EndpointIndependent, nil
}
b, err := d.DiscoverOther(other)
if err != nil {
return "", err
}
if sameAddr(b, a) {
return AddressDependent, nil
}
return AddressPortDependent, nil
}
func LocalAddrs() []*net.IPAddr {
return local
}
var local []*net.IPAddr
func init() {
ifaces, err := net.Interfaces()
if err != nil {
return
}
for _, iface := range ifaces {
addrs, _ := iface.Addrs()
for _, it := range addrs {
var ip net.IP
switch it := it.(type) {
case *net.IPNet:
ip = it.IP
case *net.IPAddr:
ip = it.IP
}
if ip != nil && ip.IsGlobalUnicast() {
local = append(local, &net.IPAddr{ip, iface.Name})
}
}
}
}