isClient bool
// constant after handshake; protected by handshakeMutex
- handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
- handshakeErr error // error resulting from handshake
- vers uint16 // TLS version
- haveVers bool // version has been negotiated
- config *Config // configuration passed to constructor
- handshakeComplete bool
- didResume bool // whether this connection was a session resumption
- cipherSuite uint16
- ocspResponse []byte // stapled OCSP response
- peerCertificates []*x509.Certificate
+ handshakeMutex sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
+ handshakeErr error // error resulting from handshake
+ vers uint16 // TLS version
+ haveVers bool // version has been negotiated
+ config *Config // configuration passed to constructor
+ handshakeComplete bool
+ didResume bool // whether this connection was a session resumption
+ extendedMasterSecret bool // whether this session used an extended master secret
+ cipherSuite uint16
+ ocspResponse []byte // stapled OCSP response
+ peerCertificates []*x509.Certificate
// verifiedChains contains the certificate chains that we built, as
// opposed to the ones presented by the server.
verifiedChains [][]*x509.Certificate
clientProtocolFallback bool
usedALPN bool
+ // verify_data values for the renegotiation extension.
+ clientVerify []byte
+ serverVerify []byte
+
channelID *ecdsa.PublicKey
// input/output
}
func (hc *halfConn) error() error {
- hc.Lock()
+ // This should be locked, but I've removed it for the renegotiation
+ // tests since we don't concurrently read and write the same tls.Conn
+ // in any case during testing.
err := hc.err
- hc.Unlock()
return err
}
func (c *Conn) readRecord(want recordType) error {
// Caller must be in sync with connection:
// handshake data if handshake not yet completed,
- // else application data. (We don't support renegotiation.)
+ // else application data.
switch want {
default:
c.sendAlert(alertInternalError)
case recordTypeHandshake:
// TODO(rsc): Should at least pick off connection close.
if typ != want {
- return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation))
+ // A client might need to process a HelloRequest from
+ // the server, thus receiving a handshake message when
+ // application data is expected is ok.
+ if !c.isClient {
+ return c.in.setErrorLocked(c.sendAlert(alertNoRenegotiation))
+ }
}
c.hand.Write(data)
}
var m handshakeMessage
switch data[0] {
+ case typeHelloRequest:
+ m = new(helloRequestMsg)
case typeClientHello:
m = &clientHelloMsg{
isDTLS: c.isDTLS,
return n + m, c.out.setErrorLocked(err)
}
+func (c *Conn) handleRenegotiation() error {
+ c.handshakeComplete = false
+ if !c.isClient {
+ panic("renegotiation should only happen for a client")
+ }
+
+ msg, err := c.readHandshake()
+ if err != nil {
+ return err
+ }
+ _, ok := msg.(*helloRequestMsg)
+ if !ok {
+ c.sendAlert(alertUnexpectedMessage)
+ return alertUnexpectedMessage
+ }
+
+ return c.Handshake()
+}
+
+func (c *Conn) Renegotiate() error {
+ if !c.isClient {
+ helloReq := new(helloRequestMsg)
+ c.writeRecord(recordTypeHandshake, helloReq.marshal())
+ }
+
+ c.handshakeComplete = false
+ return c.Handshake()
+}
+
// Read can be made to time out and return a net.Error with Timeout() == true
// after a fixed time limit; see SetDeadline and SetReadDeadline.
func (c *Conn) Read(b []byte) (n int, err error) {
// Soft error, like EAGAIN
return 0, err
}
+ if c.hand.Len() > 0 {
+ // We received handshake bytes, indicating the
+ // start of a renegotiation.
+ if err := c.handleRenegotiation(); err != nil {
+ return 0, err
+ }
+ continue
+ }
}
if err := c.in.err; err != nil {
return 0, err