func (m *mapping) tryNATPMPGW(gw net.IP, destroy bool) bool { var externalPort uint16 var actualLifetime time.Duration var err error var preferredLifetime time.Duration if destroy && !m.lIsActive() { // no point destroying if we're not active return true } else if !destroy { // lifetime is zero if we're destroying preferredLifetime = m.cfg.Lifetime } // attempt mapping externalPort, actualLifetime, err = natpmp.Map(gw, natpmp.Protocol(m.cfg.Protocol), m.cfg.InternalPort, m.cfg.ExternalPort, preferredLifetime) if err != nil { log.Infof("NAT-PMP failed: %v", err) return false } m.mutex.Lock() m.cfg.ExternalPort = externalPort m.cfg.Lifetime = actualLifetime m.mutex.Unlock() if preferredLifetime == 0 { // we have finished tearing down the mapping by mapping it with a // lifetime of zero, so return return true } expireTime := time.Now().Add(actualLifetime) // Now attempt to get the external IP. extIP, err := natpmp.GetExternalAddr(gw) m.mutex.Lock() defer m.mutex.Unlock() // update external address if err == nil { m.externalAddr = extIP.String() } m.expireTime = expireTime return true }
// Attempt to obtain the external IP address from the default gateway. // // If the host has a globally routable IP, returns that IP. // // Currently this only tries NAT-PMP and does not attempt to learn the external // IP address via UPnP. // // This function is not very useful because the IP address returned may still // be an RFC1918 address, due to the possibility of a double NAT setup. There // are better solutions for obtaining one's public IP address, such as STUN. func ExternalAddr() (net.IP, error) { if gr, ip := isGloballyRoutable(); gr { return ip, nil } gwa, err := gateway.GetIPs() if err != nil { return nil, err } var extaddr net.IP for _, gw := range gwa { extaddr, err = natpmp.GetExternalAddr(gw) if err == nil { return extaddr, nil } } return nil, err }