v2fly/v2ray-core · critical · errors.Error

invalid listen address

Error message

invalid listen address 

What it means

This panic is raised in the TLS-mirror enrollment-confirmation server's Listen() when v2net.ParseDestination fails to parse the configured listen string (ServerConfig.listen, field 3 of the roundtripperenrollmentconfirmation ServerConfig proto). ParseDestination (common/net/destination.go:30) only accepts forms like 'tcp:1.2.3.4:5678', 'udp:host:port', 'unix:/path', or a bare 'host:port' (defaulting to TCP); anything else — missing port, non-numeric port, port out of range, malformed IPv6 — returns an error, and this code converts it into a process-killing panic with AtError severity.

Source

Thrown at transport/internet/tlsmirror/mirrorenrollment/roundtripperenrollmentconfirmation/server.go:47

}

type Server struct {
	config              *ServerConfig
	ctx                 context.Context
	enrollmentProcessor tlsmirror.ConnectionEnrollmentConfirmationProcessor
	rttServer           request.RoundTripperServer
}

func (s *Server) OnConnectionEnrollmentConfirmationServerInstanceConfigReady(config tlsmirror.ConnectionEnrollmentConfirmationServerInstanceConfig) {
	s.enrollmentProcessor = config.EnrollmentProcessor
}

func (s *Server) Listen(ctx context.Context) (v2net.Listener, error) {
	transportEnvironment := envctx.EnvironmentFromContext(s.ctx).(environment.TransportEnvironment)
	listener := transportEnvironment.Listener()
	addr, err := v2net.ParseDestination(s.config.Listen)
	if err != nil {
		panic(newError("invalid listen address " + s.config.Listen).Base(err).AtError())
	}
	netaddr := &net.TCPAddr{IP: addr.Address.IP(), Port: int(addr.Port)}
	l, err := listener.Listen(s.ctx, netaddr, nil)
	if err != nil {
		panic(newError("failed to listen on " + s.config.Listen).Base(err).AtError())
	}
	return l, nil
}

func (s *Server) OnRoundTrip(ctx context.Context, req request.Request, opts ...request.RoundTripperOption) (resp request.Response, err error) {
	enrollmentReq := &tlsmirror.EnrollmentConfirmationReq{}
	err = proto.Unmarshal(req.Data, enrollmentReq)
	if err != nil {
		return request.Response{}, newError("failed to unmarshal enrollment confirmation request").Base(err).AtError()
	}
	enrollmentResp, err := s.enrollmentProcessor.VerifyConnectionEnrollment(enrollmentReq)
	if err != nil {
		return request.Response{}, newError("failed to process enrollment confirmation request").Base(err).AtError()

View on GitHub (pinned to db12914161)

Solutions

  1. Fix the listen string to the exact expected format 'tcp:IP:PORT' (e.g. 'tcp:127.0.0.1:9443'); note the prefix has a single colon, no slashes.
  2. Validate the address before startup with v2net.ParseDestination in your config loader (or a unit test over shipped configs) so errors surface as config errors, not panics.
  3. For IPv6, use bracketed host: 'tcp:[::1]:9443'.
  4. If you meant a Unix socket, use the 'unix:/path/to/socket' form — but be aware this code then builds a net.TCPAddr from addr.Address.IP(), so for this server stick to tcp with a literal IP.

Example fix

// before (config)
{
  "listen": "127.0.0.1"   // ParseDestination fails: missing port -> panic
}

// after
{
  "listen": "tcp:127.0.0.1:9443"
}
Defensive patterns

Strategy: validation

Validate before calling

// validate the enrollment-confirmation listen string before starting the server:
dest, err := net.ParseDestination(cfg.Listen)
if err != nil {
    return fmt.Errorf("invalid enrollment listen address %q: %w", cfg.Listen, err)
}
if dest.Network != net.Network_TCP {
    return fmt.Errorf("enrollment listen must be tcp, got %q", cfg.Listen)
}
// optional: reject hostnames, this server binds a literal TCPAddr
if !dest.Address.IsIP() {
    return fmt.Errorf("enrollment listen must use a literal IP, got %q", dest.Address.String())
}

Type guard

func isValidListenAddress(s string) bool {
    d, err := net.ParseDestination(s)
    return err == nil && d.Network == net.Network_TCP && d.Address.IsIP() && d.Port > 0
}

Try / catch

// wrap server startup so a bad config surfaces as an error, not a crash:
func startEnrollmentServer(s *rec.Server) (err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("enrollment server startup failed: %v", r)
        }
    }()
    l, err := s.Listen(ctx)
    if err != nil {
        return err
    }
    _ = l // serve...
    return nil
}

Prevention

When it happens

Trigger: Setting ServerConfig.listen to a bare IP or hostname with no port ('127.0.0.1'); a port that is not a number or exceeds 65535 ('tcp:0.0.0.0:99999'); IPv6 without brackets or the tcp: prefix ('::1:443'); leaving the field empty-ish with stray whitespace; JSON/TOML config typos where the value is a number or an object rather than the expected string.

Common situations: First-time setup of the tlsmirror enrollment confirmation component where users copy an address format from another tool (e.g. 'localhost' alone, or a URL like 'tcp://host:port' with a double slash); config generated by scripts that concatenate host and port incorrectly; version migrations where the listen key was renamed and silently ends up zero-valued/empty.

Related errors


AI-assisted analysis of v2fly/v2ray-core@db12914161 (2026-08-15). Data as JSON: /api/errors/179463956c895221. Report an issue: GitHub.