//go:build go1.25 && !without_badtls package badtls import ( "bytes" "os" "reflect" "sync/atomic" "unsafe" E "github.com/sagernet/sing/common/exceptions" "github.com/sagernet/sing/common/tls" ) type RawConn struct { pointer unsafe.Pointer methods *Methods IsClient *bool IsHandshakeComplete *atomic.Bool Vers *uint16 CipherSuite *uint16 RawInput *bytes.Buffer Input *bytes.Reader Hand *bytes.Buffer CloseNotifySent *bool CloseNotifyErr *error In *RawHalfConn Out *RawHalfConn BytesSent *int64 PacketsSent *int64 ActiveCall *atomic.Int32 } func NewRawConn(rawTLSConn tls.Conn) (*RawConn, error) { var ( pointer unsafe.Pointer methods *Methods loaded bool ) for _, tlsCreator := range methodRegistry { pointer, methods, loaded = tlsCreator(rawTLSConn) if loaded { break } } if !loaded { return nil, os.ErrInvalid } conn := &RawConn{ pointer: pointer, methods: methods, } rawConn := reflect.Indirect(reflect.ValueOf(rawTLSConn)) rawIsClient := rawConn.FieldByName("isClient") if !rawIsClient.IsValid() || rawIsClient.Kind() != reflect.Bool { return nil, E.New("invalid Conn.isClient") } conn.IsClient = (*bool)(unsafe.Pointer(rawIsClient.UnsafeAddr())) rawIsHandshakeComplete := rawConn.FieldByName("isHandshakeComplete") if !rawIsHandshakeComplete.IsValid() || rawIsHandshakeComplete.Kind() != reflect.Struct { return nil, E.New("invalid Conn.isHandshakeComplete") } conn.IsHandshakeComplete = (*atomic.Bool)(unsafe.Pointer(rawIsHandshakeComplete.UnsafeAddr())) rawVers := rawConn.FieldByName("vers") if !rawVers.IsValid() || rawVers.Kind() != reflect.Uint16 { return nil, E.New("invalid Conn.vers") } conn.Vers = (*uint16)(unsafe.Pointer(rawVers.UnsafeAddr())) rawCipherSuite := rawConn.FieldByName("cipherSuite") if !rawCipherSuite.IsValid() || rawCipherSuite.Kind() != reflect.Uint16 { return nil, E.New("invalid Conn.cipherSuite") } conn.CipherSuite = (*uint16)(unsafe.Pointer(rawCipherSuite.UnsafeAddr())) rawRawInput := rawConn.FieldByName("rawInput") if !rawRawInput.IsValid() || rawRawInput.Kind() != reflect.Struct { return nil, E.New("invalid Conn.rawInput") } conn.RawInput = (*bytes.Buffer)(unsafe.Pointer(rawRawInput.UnsafeAddr())) rawInput := rawConn.FieldByName("input") if !rawInput.IsValid() || rawInput.Kind() != reflect.Struct { return nil, E.New("invalid Conn.input") } conn.Input = (*bytes.Reader)(unsafe.Pointer(rawInput.UnsafeAddr())) rawHand := rawConn.FieldByName("hand") if !rawHand.IsValid() || rawHand.Kind() != reflect.Struct { return nil, E.New("invalid Conn.hand") } conn.Hand = (*bytes.Buffer)(unsafe.Pointer(rawHand.UnsafeAddr())) rawCloseNotifySent := rawConn.FieldByName("closeNotifySent") if !rawCloseNotifySent.IsValid() || rawCloseNotifySent.Kind() != reflect.Bool { return nil, E.New("invalid Conn.closeNotifySent") } conn.CloseNotifySent = (*bool)(unsafe.Pointer(rawCloseNotifySent.UnsafeAddr())) rawCloseNotifyErr := rawConn.FieldByName("closeNotifyErr") if !rawCloseNotifyErr.IsValid() || rawCloseNotifyErr.Kind() != reflect.Interface { return nil, E.New("invalid Conn.closeNotifyErr") } conn.CloseNotifyErr = (*error)(unsafe.Pointer(rawCloseNotifyErr.UnsafeAddr())) rawIn := rawConn.FieldByName("in") if !rawIn.IsValid() || rawIn.Kind() != reflect.Struct { return nil, E.New("invalid Conn.in") } halfIn, err := NewRawHalfConn(rawIn, methods) if err != nil { return nil, E.Cause(err, "invalid Conn.in") } conn.In = halfIn rawOut := rawConn.FieldByName("out") if !rawOut.IsValid() || rawOut.Kind() != reflect.Struct { return nil, E.New("invalid Conn.out") } halfOut, err := NewRawHalfConn(rawOut, methods) if err != nil { return nil, E.Cause(err, "invalid Conn.out") } conn.Out = halfOut rawBytesSent := rawConn.FieldByName("bytesSent") if !rawBytesSent.IsValid() || rawBytesSent.Kind() != reflect.Int64 { return nil, E.New("invalid Conn.bytesSent") } conn.BytesSent = (*int64)(unsafe.Pointer(rawBytesSent.UnsafeAddr())) rawPacketsSent := rawConn.FieldByName("packetsSent") if !rawPacketsSent.IsValid() || rawPacketsSent.Kind() != reflect.Int64 { return nil, E.New("invalid Conn.packetsSent") } conn.PacketsSent = (*int64)(unsafe.Pointer(rawPacketsSent.UnsafeAddr())) rawActiveCall := rawConn.FieldByName("activeCall") if !rawActiveCall.IsValid() || rawActiveCall.Kind() != reflect.Struct { return nil, E.New("invalid Conn.activeCall") } conn.ActiveCall = (*atomic.Int32)(unsafe.Pointer(rawActiveCall.UnsafeAddr())) return conn, nil } func (c *RawConn) ReadRecord() error { return c.methods.readRecord(c.pointer) } func (c *RawConn) HandlePostHandshakeMessage() error { return c.methods.handlePostHandshakeMessage(c.pointer) } func (c *RawConn) WriteRecordLocked(typ uint16, data []byte) (int, error) { return c.methods.writeRecordLocked(c.pointer, typ, data) }