Search code examples
socketsgonetwork-programmingembedding

Intercept tls.Conn's Read() and Write() while retaining tls.Conn functionality


I have the following piece of code:

l, err := tls.Listen("tcp", "localhost:0", cfg)
dieIf(err)
c, err := l.Accept()
dieIf(err)
err = c.(*tls.Conn).Handshake()
dieIf(err)

It works just fine, but I'd like to intercept tls.Conn's reads and writes.

I thought about doing this:

type MitmConn struct {
  net.Conn
}

func (self *MitmConn) Read(b []byte) (n int, err error) {
  ...
}

func (self *MitmConn) Write(b []byte) (n int, err error) {
  ...
}

l, err := tls.Listen("tcp", "localhost:0", cfg)
dieIf(err)
c, err := l.Accept()
dieIf(err)
c = &MitmConn{c}

But then, this would fail:

// panic: interface conversion: net.Conn is *MitmConn, not *tls.Conn
err = c.(*tls.Conn).Handshake()
dieIf(err)

Any ideas?


Solution

  • @kostix's answer suffices for most cases, but if you want to intercept tls.Conn.Handshake()'s reads and writes, you need to inject your mitmConn wrapper one level beneath.

    Here's a working example:

    package main
    
    import "crypto/tls"
    
    func dieIf(err error) {
        if err != nil {
            panic(err)
        }
    }
    
    type mitmConn struct {
        // Since we went one level beneath, no need for *tls.Conn here
        net.Conn
    }
    
    func (mc *mitmConn) Read(b []byte) (n int, err error) {
      return 0, nil
    }
    
    func (mc *mitmConn) Write(b []byte) (n int, err error) {
      return 0, nil
    }
    
    func main() {
        // Don't use tls.Listen
        l, err := net.Listen("tcp", "localhost:0")
        dieIf(err)
        c, err := l.Accept()
        dieIf(err)
        // Make a new tls.Conn. 
        // After the tls.Server() call, the nesting of Conn 
        // objects looks will be:
        // - *tls.Conn
        //  - mitmConn
        //   - *net.TCPConn
        c = tls.Server(mitmConn{c}, nil)
        // Since tls.Server returns a net.Conn, not a *tls.Conn, we need to cast
        err = c.(*tls.Conn).Handshake()
        dieIf(err)
    }