projectdiscovery/nuclei · error

goimpacket: refusing to dial %s/%s without an executionId-bo

Error message

goimpacket: refusing to dial %s/%s without an executionId-bound dialer; wrap the call site with a *gptransport.Dialer built via NewExecDialer

What it means

A fail-closed tripwire installed by gptransport's init(): it replaces goimpacket's global dial function so that any SMB/DCOM-style connection attempted without a per-execution binding is refused instead of silently escaping nuclei's network policy. It fires when goimpacket falls back to the global dialer because the call site did not use a *gptr.Dialer built by gptransport.NewExecDialer(execID), or the context carries no 'executionId' key.

Source

Thrown at pkg/js/libs/gptransport/dialer.go:23

// the global tripwire: any goimpacket dial without an execution-bound Dialer
// fails closed instead of leaking across scans.
package gptransport

import (
	"context"
	"fmt"
	"net"

	gptr "github.com/Mzack9999/goimpacket/pkg/transport"

	"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
)

func init() {
	gptr.SetDial(func(ctx context.Context, network, address string) (net.Conn, error) {
		execID := ExecutionIDFromCtx(ctx)
		if execID == "" {
			return nil, fmt.Errorf("goimpacket: refusing to dial %s/%s without an executionId-bound dialer; wrap the call site with a *gptransport.Dialer built via NewExecDialer", network, address)
		}
		return DialWithExec(ctx, execID, network, address)
	})
}

// NewExecDialer returns a *gptr.Dialer whose DialFn is bound to the
// given executionId. Every connection made through the returned dialer is
// validated against the execution's network policy and routed through the
// matching fastdialer.
func NewExecDialer(execID string) *gptr.Dialer {
	if execID == "" {
		return &gptr.Dialer{}
	}
	return &gptr.Dialer{
		DialFn: func(ctx context.Context, network, address string) (net.Conn, error) {
			return DialWithExec(ctx, execID, network, address)
		},
	}

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Wrap the goimpacket call site: dialer := gptransport.NewExecDialer(execID) and pass that *gptr.Dialer into the goimpacket client/transport so every dial is execution-bound
  2. Propagate the execution context: ctx = context.WithValue(ctx, "executionId", execID) so ExecutionIDFromCtx can recover the id
  3. Verify protocolstate dialers are registered for that executionId before dialing (otherwise you hit 'no fastdialer registered' next)
  4. In tests, construct clients with an explicit exec-bound Dialer instead of relying on the global dial function

Example fix

// before: uses goimpacket's global dial -> refused
client := smbclient.New(target)

// after: every dial is bound to the execution and its network policy
execID := gptransport.ExecutionIDFromCtx(ctx)
client := smbclient.New(target, gptransport.NewExecDialer(execID))
Defensive patterns

Strategy: validation

Validate before calling

execID := gptransport.ExecutionIDFromCtx(ctx)
if execID == "" {
    return errors.New("ctx carries no executionId; bind the call site with gptransport.NewExecDialer")
}
dialer := gptransport.NewExecDialer(execID) // hand this dialer to the goimpacket client

Type guard

func isExecutionBound(ctx context.Context) bool {
    return gptransport.ExecutionIDFromCtx(ctx) != ""
}

Try / catch

conn, err := dial(ctx, network, addr)
if err != nil {
    if strings.Contains(err.Error(), "executionId-bound dialer") {
        // call site not wrapped: rebuild the client with gptransport.NewExecDialer(execID); do not bypass
    }
    return err
}

Prevention

When it happens

Trigger: Calling goimpacket transport dial APIs directly (not via a Dialer from NewExecDialer) in a binary that imports pkg/js/libs/gptransport (e.g. through smbsession/dcerpc libs); dialing with context.Background() or any ctx where ExecutionIDFromCtx returns ''.

Common situations: Embedding nuclei via lib/ with goimpacket clients created before per-execution dialers existed; new JS protocol libs (smb/dcerpc/winrm) that forget to wrap their connection with NewExecDialer; unit tests that dial with a bare context; goroutines that drop the scan context.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/a955492360d7af41. Report an issue: GitHub.