// DialArgs is a convenience function returning arguments for use in net.Dial func DialArgs(m ma.Multiaddr) (string, string, error) { if !IsThinWaist(m) { return "", "", fmt.Errorf("%s is not a 'thin waist' address", m) } str := m.String() parts := strings.Split(str, "/")[1:] if len(parts) == 2 { // only IP return parts[0], parts[1], nil } network := parts[2] if parts[2] == "udp" && len(parts) > 4 && parts[4] == "utp" { network = parts[4] } var host string switch parts[0] { case "ip4": network = network + "4" host = strings.Join([]string{parts[1], parts[3]}, ":") case "ip6": network = network + "6" host = fmt.Sprintf("[%s]:%s", parts[1], parts[3]) } return network, host, nil }
func (oas *ObservedAddrSet) Add(addr ma.Multiaddr, observer ma.Multiaddr) { oas.Lock() defer oas.Unlock() // for zero-value. if oas.addrs == nil { oas.addrs = make(map[string]*ObservedAddr) oas.ttl = peer.OwnObservedAddrTTL } s := addr.String() oa, found := oas.addrs[s] // first time seeing address. if !found { oa = &ObservedAddr{ Addr: addr, SeenBy: make(map[string]struct{}), } oas.addrs[s] = oa } // mark the observer oa.SeenBy[observerGroup(observer)] = struct{}{} oa.LastSeen = time.Now() }
// rawConnDial dials the underlying net.Conn + manet.Conns func (d *Dialer) rawConnDial(ctx context.Context, raddr ma.Multiaddr, remote peer.ID) (manet.Conn, error) { // before doing anything, check we're going to be able to dial. // we may not support the given address. if _, _, err := manet.DialArgs(raddr); err != nil { return nil, err } if strings.HasPrefix(raddr.String(), "/ip4/0.0.0.0") { log.Event(ctx, "connDialZeroAddr", lgbl.Dial("conn", d.LocalPeer, remote, nil, raddr)) return nil, fmt.Errorf("Attempted to connect to zero address: %s", raddr) } // get local addr to use. laddr := pickLocalAddr(d.LocalAddrs, raddr) logdial := lgbl.Dial("conn", d.LocalPeer, remote, laddr, raddr) defer log.EventBegin(ctx, "connDialRawConn", logdial).Done() // make a copy of the manet.Dialer, we may need to change its timeout. madialer := d.Dialer if laddr != nil && reuseportIsAvailable() { // we're perhaps going to dial twice. half the timeout, so we can afford to. // otherwise our context would expire right after the first dial. madialer.Dialer.Timeout = (madialer.Dialer.Timeout / 2) // dial using reuseport.Dialer, because we're probably reusing addrs. // this is optimistic, as the reuseDial may fail to bind the port. rpev := log.EventBegin(ctx, "connDialReusePort", logdial) if nconn, retry, reuseErr := reuseDial(madialer.Dialer, laddr, raddr); reuseErr == nil { // if it worked, wrap the raw net.Conn with our manet.Conn logdial["reuseport"] = "success" rpev.Done() return manet.WrapNetConn(nconn) } else if !retry { // reuseDial is sure this is a legitimate dial failure, not a reuseport failure. logdial["reuseport"] = "failure" logdial["error"] = reuseErr rpev.Done() return nil, reuseErr } else { // this is a failure to reuse port. log it. logdial["reuseport"] = "retry" logdial["error"] = reuseErr rpev.Done() } } defer log.EventBegin(ctx, "connDialManet", logdial).Done() return madialer.Dial(raddr) }
func outfmt(m ma.Multiaddr) string { switch format { case "string": return m.String() case "slice": return fmt.Sprintf("%v", m.Bytes()) case "bytes": return string(m.Bytes()) case "hex": return "0x" + hex.EncodeToString(m.Bytes()) } die("error: invalid format", format) return "" }
func addPortMapping(nmgr *natManager, intaddr ma.Multiaddr) { nat := nmgr.NAT() if nat == nil { panic("natManager addPortMapping called without a nat.") } // first, check if the port mapping already exists. for _, mapping := range nat.Mappings() { if mapping.InternalAddr().Equal(intaddr) { return // it exists! return. } } ctx := context.TODO() lm := make(lgbl.DeferredMap) lm["internalAddr"] = func() interface{} { return intaddr.String() } defer log.EventBegin(ctx, "natMgrAddPortMappingWait", lm).Done() select { case <-nmgr.proc.Closing(): lm["outcome"] = "cancelled" return // no use. case <-nmgr.ready: // wait until it's ready. } // actually start the port map (sub-event because waiting may take a while) defer log.EventBegin(ctx, "natMgrAddPortMapping", lm).Done() // get the nat m, err := nat.NewMapping(intaddr) if err != nil { lm["outcome"] = "failure" lm["error"] = err return } extaddr, err := m.ExternalAddr() if err != nil { lm["outcome"] = "failure" lm["error"] = err return } lm["outcome"] = "success" lm["externalAddr"] = func() interface{} { return extaddr.String() } log.Infof("established nat port mapping: %s <--> %s", intaddr, extaddr) }
// Dial metadata is metadata for dial events func Dial(sys string, lid, rid peer.ID, laddr, raddr ma.Multiaddr) DeferredMap { m := DeferredMap{} m["subsystem"] = sys if lid != "" { m["localPeer"] = func() interface{} { return lid.Pretty() } } if laddr != nil { m["localAddr"] = func() interface{} { return laddr.String() } } if rid != "" { m["remotePeer"] = func() interface{} { return rid.Pretty() } } if raddr != nil { m["remoteAddr"] = func() interface{} { return raddr.String() } } return m }
// NewMapping attemps to construct a mapping on protocol and internal port // It will also periodically renew the mapping until the returned Mapping // -- or its parent NAT -- is Closed. // // May not succeed, and mappings may change over time; // NAT devices may not respect our port requests, and even lie. // Clients should not store the mapped results, but rather always // poll our object for the latest mappings. func (nat *NAT) NewMapping(maddr ma.Multiaddr) (Mapping, error) { if nat == nil { return nil, fmt.Errorf("no nat available") } network, addr, err := manet.DialArgs(maddr) if err != nil { return nil, fmt.Errorf("DialArgs failed on addr:", maddr.String()) } switch network { case "tcp", "tcp4", "tcp6": network = "tcp" case "udp", "udp4", "udp6": network = "udp" default: return nil, fmt.Errorf("transport not supported by NAT: %s", network) } intports := strings.Split(addr, ":")[1] intport, err := strconv.Atoi(intports) if err != nil { return nil, err } m := &mapping{ nat: nat, proto: network, intport: intport, intaddr: maddr, } m.proc = goprocess.WithTeardown(func() error { nat.rmMapping(m) return nil }) nat.addMapping(m) m.proc.AddChild(periodic.Every(MappingDuration/3, func(worker goprocess.Process) { nat.establishMapping(m) })) // do it once synchronously, so first mapping is done right away, and before exiting, // allowing users -- in the optimistic case -- to use results right after. nat.establishMapping(m) return m, nil }