// Package reality implements TLS scanning to find servers suitable for REALITY protocol. package reality import ( "context" "crypto/tls" "fmt" "net" "strings" "time" ) // Host represents a target to scan. type Host struct { IP net.IP Origin string // original input (domain, IP, or CIDR string) } // ScanResult holds the outcome of a TLS scan. type ScanResult struct { IP string Origin string CertDomain string // certificate Subject.CommonName CertIssuer string // pipe-delimited Issuer.Organization GeoCode string // ISO country code, filled in by caller TLSVersion uint16 ALPN string Latency time.Duration // TLS handshake duration Feasible bool Error error } // ScanConfig controls scanning behavior. type ScanConfig struct { Port int Timeout time.Duration } // ScanHost performs a TLS handshake against the given host and checks REALITY feasibility. func ScanHost(ctx context.Context, host Host, cfg ScanConfig) ScanResult { result := ScanResult{ IP: host.IP.String(), Origin: host.Origin, } addr := fmt.Sprintf("%s:%d", host.IP.String(), cfg.Port) tlsCfg := &tls.Config{ InsecureSkipVerify: true, NextProtos: []string{"h2", "http/1.1"}, CurvePreferences: []tls.CurveID{tls.X25519}, } // Set SNI if the origin is a domain name. if net.ParseIP(host.Origin) == nil { tlsCfg.ServerName = host.Origin } dialer := &net.Dialer{Timeout: cfg.Timeout} conn, err := dialer.DialContext(ctx, "tcp", addr) if err != nil { result.Error = fmt.Errorf("dial: %w", err) return result } defer conn.Close() _ = conn.SetDeadline(time.Now().Add(cfg.Timeout)) tlsConn := tls.Client(conn, tlsCfg) hsStart := time.Now() if err := tlsConn.HandshakeContext(ctx); err != nil { result.Error = fmt.Errorf("tls handshake: %w", err) return result } result.Latency = time.Since(hsStart) defer tlsConn.Close() state := tlsConn.ConnectionState() result.TLSVersion = state.Version result.ALPN = state.NegotiatedProtocol if len(state.PeerCertificates) > 0 { cert := state.PeerCertificates[0] result.CertDomain = cert.Subject.CommonName if len(cert.Issuer.Organization) > 0 { result.CertIssuer = strings.Join(cert.Issuer.Organization, " | ") } } result.Feasible = result.TLSVersion == tls.VersionTLS13 && result.ALPN == "h2" && result.CertDomain != "" && result.CertIssuer != "" return result } // TLSVersionName returns a human-readable name for a TLS version. func TLSVersionName(v uint16) string { switch v { case tls.VersionTLS10: return "TLS 1.0" case tls.VersionTLS11: return "TLS 1.1" case tls.VersionTLS12: return "TLS 1.2" case tls.VersionTLS13: return "TLS 1.3" default: return fmt.Sprintf("0x%04x", v) } }