package autonat

import (
	
	
	
	
	

	
	
	
	

	

	ma 
)

var streamTimeout = 60 * time.Second

const (
	ServiceName = "libp2p.autonat"

	maxMsgSize = 4096
)

// AutoNATService provides NAT autodetection services to other peers
type autoNATService struct {
	instanceLock      sync.Mutex
	instance          context.CancelFunc
	backgroundRunning chan struct{} // closed when background exits

	config *config

	// rate limiter
	mx         sync.Mutex
	reqs       map[peer.ID]int
	globalReqs int
}

// NewAutoNATService creates a new AutoNATService instance attached to a host
func newAutoNATService( *config) (*autoNATService, error) {
	if .dialer == nil {
		return nil, errors.New("cannot create NAT service without a network")
	}
	return &autoNATService{
		config: ,
		reqs:   make(map[peer.ID]int),
	}, nil
}

func ( *autoNATService) ( network.Stream) {
	if  := .Scope().SetService(ServiceName);  != nil {
		log.Debugf("error attaching stream to autonat service: %s", )
		.Reset()
		return
	}

	if  := .Scope().ReserveMemory(maxMsgSize, network.ReservationPriorityAlways);  != nil {
		log.Debugf("error reserving memory for autonat stream: %s", )
		.Reset()
		return
	}
	defer .Scope().ReleaseMemory(maxMsgSize)

	.SetDeadline(time.Now().Add(streamTimeout))
	defer .Close()

	 := .Conn().RemotePeer()
	log.Debugf("New stream from %s", )

	 := pbio.NewDelimitedReader(, maxMsgSize)
	 := pbio.NewDelimitedWriter()

	var  pb.Message
	var  pb.Message

	 := .ReadMsg(&)
	if  != nil {
		log.Debugf("Error reading message from %s: %s", , .Error())
		.Reset()
		return
	}

	 := .GetType()
	if  != pb.Message_DIAL {
		log.Debugf("Unexpected message from %s: %s (%d)", , .String(), )
		.Reset()
		return
	}

	 := .handleDial(, .Conn().RemoteMultiaddr(), .GetDial().GetPeer())
	.Type = pb.Message_DIAL_RESPONSE.Enum()
	.DialResponse = 

	 = .WriteMsg(&)
	if  != nil {
		log.Debugf("Error writing response to %s: %s", , .Error())
		.Reset()
		return
	}
	if .config.metricsTracer != nil {
		.config.metricsTracer.OutgoingDialResponse(.GetDialResponse().GetStatus())
	}
}

func ( *autoNATService) ( peer.ID,  ma.Multiaddr,  *pb.Message_PeerInfo) *pb.Message_DialResponse {
	if  == nil {
		return newDialResponseError(pb.Message_E_BAD_REQUEST, "missing peer info")
	}

	 := .GetId()
	if  != nil {
		,  := peer.IDFromBytes()
		if  != nil {
			return newDialResponseError(pb.Message_E_BAD_REQUEST, "bad peer id")
		}

		if  !=  {
			return newDialResponseError(pb.Message_E_BAD_REQUEST, "peer id mismatch")
		}
	}

	 := make([]ma.Multiaddr, 0, .config.maxPeerAddresses)
	 := make(map[string]struct{})

	// Don't even try to dial peers with blocked remote addresses. In order to dial a peer, we
	// need to know their public IP address, and it needs to be different from our public IP
	// address.
	if .config.dialPolicy.skipDial() {
		if .config.metricsTracer != nil {
			.config.metricsTracer.OutgoingDialRefused(dial_blocked)
		}
		// Note: versions < v0.20.0 return Message_E_DIAL_ERROR here, thus we can not rely on this error code.
		return newDialResponseError(pb.Message_E_DIAL_REFUSED, "refusing to dial peer with blocked observed address")
	}

	// Determine the peer's IP address.
	,  := ma.SplitFirst()
	switch .Protocol().Code {
	case ma.P_IP4, ma.P_IP6:
	default:
		// This shouldn't be possible as we should skip all addresses that don't include
		// public IP addresses.
		return newDialResponseError(pb.Message_E_INTERNAL_ERROR, "expected an IP address")
	}

	// add observed addr to the list of addresses to dial
	 = append(, )
	[.String()] = struct{}{}

	for ,  := range .GetAddrs() {
		,  := ma.NewMultiaddrBytes()
		if  != nil {
			log.Debugf("Error parsing multiaddr: %s", .Error())
			continue
		}

		// For security reasons, we _only_ dial the observed IP address.
		// Replace other IP addresses with the observed one so we can still try the
		// requested ports/transports.
		if ,  := ma.SplitFirst(); !.Equal() {
			// Make sure it's an IP address
			switch .Protocol().Code {
			case ma.P_IP4, ma.P_IP6:
			default:
				continue
			}
			 = .Multiaddr()
			if len() > 0 {
				 = .Encapsulate()
			}
		}

		// Make sure we're willing to dial the rest of the address (e.g., not a circuit
		// address).
		if .config.dialPolicy.skipDial() {
			continue
		}

		 := .String()
		,  := []
		if  {
			continue
		}

		 = append(, )
		[] = struct{}{}

		if len() >= .config.maxPeerAddresses {
			break
		}
	}

	if len() == 0 {
		if .config.metricsTracer != nil {
			.config.metricsTracer.OutgoingDialRefused(no_valid_address)
		}
		// Note: versions < v0.20.0 return Message_E_DIAL_ERROR here, thus we can not rely on this error code.
		return newDialResponseError(pb.Message_E_DIAL_REFUSED, "no dialable addresses")
	}

	return .doDial(peer.AddrInfo{ID: , Addrs: })
}

func ( *autoNATService) ( peer.AddrInfo) *pb.Message_DialResponse {
	// rate limit check
	.mx.Lock()
	 := .reqs[.ID]
	if  >= .config.throttlePeerMax || (.config.throttleGlobalMax > 0 &&
		.globalReqs >= .config.throttleGlobalMax) {
		.mx.Unlock()
		if .config.metricsTracer != nil {
			.config.metricsTracer.OutgoingDialRefused(rate_limited)
		}
		return newDialResponseError(pb.Message_E_DIAL_REFUSED, "too many dials")
	}
	.reqs[.ID] =  + 1
	.globalReqs++
	.mx.Unlock()

	,  := context.WithTimeout(context.Background(), .config.dialTimeout)
	defer ()

	.config.dialer.Peerstore().ClearAddrs(.ID)

	.config.dialer.Peerstore().AddAddrs(.ID, .Addrs, peerstore.TempAddrTTL)

	defer func() {
		.config.dialer.Peerstore().ClearAddrs(.ID)
		.config.dialer.Peerstore().RemovePeer(.ID)
	}()

	,  := .config.dialer.DialPeer(, .ID)
	if  != nil {
		log.Debugf("error dialing %s: %s", .ID, .Error())
		// wait for the context to timeout to avoid leaking timing information
		// this renders the service ineffective as a port scanner
		<-.Done()
		return newDialResponseError(pb.Message_E_DIAL_ERROR, "dial failed")
	}

	 := .RemoteMultiaddr()
	.config.dialer.ClosePeer(.ID)
	return newDialResponseOK()
}

// Enable the autoNAT service if it is not running.
func ( *autoNATService) () {
	.instanceLock.Lock()
	defer .instanceLock.Unlock()
	if .instance != nil {
		return
	}
	,  := context.WithCancel(context.Background())
	.instance = 
	.backgroundRunning = make(chan struct{})
	.config.host.SetStreamHandler(AutoNATProto, .handleStream)

	go .background()
}

// Disable the autoNAT service if it is running.
func ( *autoNATService) () {
	.instanceLock.Lock()
	defer .instanceLock.Unlock()
	if .instance != nil {
		.config.host.RemoveStreamHandler(AutoNATProto)
		.instance()
		.instance = nil
		<-.backgroundRunning
	}
}

func ( *autoNATService) () error {
	.Disable()
	return .config.dialer.Close()
}

func ( *autoNATService) ( context.Context) {
	defer close(.backgroundRunning)

	 := time.NewTimer(.config.throttleResetPeriod)
	defer .Stop()

	for {
		select {
		case <-.C:
			.mx.Lock()
			.reqs = make(map[peer.ID]int)
			.globalReqs = 0
			.mx.Unlock()
			 := rand.Float32() * float32(.config.throttleResetJitter)
			.Reset(.config.throttleResetPeriod + time.Duration(int64()))
		case <-.Done():
			return
		}
	}
}