| // Copyright 2009 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package tls |
| |
| import ( |
| "fmt"; |
| "hash"; |
| "io"; |
| ) |
| |
| // writerEnableApplicationData is a message which instructs recordWriter to |
| // start reading and transmitting data from the application data channel. |
| type writerEnableApplicationData struct{} |
| |
| // writerChangeCipherSpec updates the encryption and MAC functions and resets |
| // the sequence count. |
| type writerChangeCipherSpec struct { |
| encryptor encryptor; |
| mac hash.Hash; |
| } |
| |
| // writerSetVersion sets the version number bytes that we included in the |
| // record header for future records. |
| type writerSetVersion struct { |
| major, minor uint8; |
| } |
| |
| // A recordWriter accepts messages from the handshake processor and |
| // application data. It writes them to the outgoing connection and blocks on |
| // writing. It doesn't read from the application data channel until the |
| // handshake processor has signaled that the handshake is complete. |
| type recordWriter struct { |
| writer io.Writer; |
| encryptor encryptor; |
| mac hash.Hash; |
| seqNum uint64; |
| major, minor uint8; |
| shutdown bool; |
| appChan <-chan []byte; |
| controlChan <-chan interface{}; |
| header [13]byte; |
| } |
| |
| func (w *recordWriter) loop(writer io.Writer, appChan <-chan []byte, controlChan <-chan interface{}) { |
| w.writer = writer; |
| w.encryptor = nop{}; |
| w.mac = nop{}; |
| w.appChan = appChan; |
| w.controlChan = controlChan; |
| |
| for !w.shutdown { |
| msg := <-controlChan; |
| if _, ok := msg.(writerEnableApplicationData); ok { |
| break |
| } |
| w.processControlMessage(msg); |
| } |
| |
| for !w.shutdown { |
| // Always process control messages first. |
| if controlMsg, ok := <-controlChan; ok { |
| w.processControlMessage(controlMsg); |
| continue; |
| } |
| |
| select { |
| case controlMsg := <-controlChan: |
| w.processControlMessage(controlMsg) |
| case appMsg := <-appChan: |
| w.processAppMessage(appMsg) |
| } |
| } |
| |
| if !closed(appChan) { |
| go func() { |
| for _ = range appChan { |
| } |
| }() |
| } |
| if !closed(controlChan) { |
| go func() { |
| for _ = range controlChan { |
| } |
| }() |
| } |
| } |
| |
| // fillMACHeader generates a MAC header. See RFC 4346, section 6.2.3.1. |
| func fillMACHeader(header *[13]byte, seqNum uint64, length int, r *record) { |
| header[0] = uint8(seqNum >> 56); |
| header[1] = uint8(seqNum >> 48); |
| header[2] = uint8(seqNum >> 40); |
| header[3] = uint8(seqNum >> 32); |
| header[4] = uint8(seqNum >> 24); |
| header[5] = uint8(seqNum >> 16); |
| header[6] = uint8(seqNum >> 8); |
| header[7] = uint8(seqNum); |
| header[8] = uint8(r.contentType); |
| header[9] = r.major; |
| header[10] = r.minor; |
| header[11] = uint8(length >> 8); |
| header[12] = uint8(length); |
| } |
| |
| func (w *recordWriter) writeRecord(r *record) { |
| w.mac.Reset(); |
| |
| fillMACHeader(&w.header, w.seqNum, len(r.payload), r); |
| |
| w.mac.Write(w.header[0:13]); |
| w.mac.Write(r.payload); |
| macBytes := w.mac.Sum(); |
| |
| w.encryptor.XORKeyStream(r.payload); |
| w.encryptor.XORKeyStream(macBytes); |
| |
| length := len(r.payload) + len(macBytes); |
| w.header[11] = uint8(length >> 8); |
| w.header[12] = uint8(length); |
| w.writer.Write(w.header[8:13]); |
| w.writer.Write(r.payload); |
| w.writer.Write(macBytes); |
| |
| w.seqNum++; |
| } |
| |
| func (w *recordWriter) processControlMessage(controlMsg interface{}) { |
| if controlMsg == nil { |
| w.shutdown = true; |
| return; |
| } |
| |
| switch msg := controlMsg.(type) { |
| case writerChangeCipherSpec: |
| w.writeRecord(&record{recordTypeChangeCipherSpec, w.major, w.minor, []byte{0x01}}); |
| w.encryptor = msg.encryptor; |
| w.mac = msg.mac; |
| w.seqNum = 0; |
| case writerSetVersion: |
| w.major = msg.major; |
| w.minor = msg.minor; |
| case alert: |
| w.writeRecord(&record{recordTypeAlert, w.major, w.minor, []byte{byte(msg.level), byte(msg.error)}}) |
| case handshakeMessage: |
| // TODO(agl): marshal may return a slice too large for a single record. |
| w.writeRecord(&record{recordTypeHandshake, w.major, w.minor, msg.marshal()}) |
| default: |
| fmt.Printf("processControlMessage: unknown %#v\n", msg) |
| } |
| } |
| |
| func (w *recordWriter) processAppMessage(appMsg []byte) { |
| if closed(w.appChan) { |
| w.writeRecord(&record{recordTypeApplicationData, w.major, w.minor, []byte{byte(alertCloseNotify)}}); |
| w.shutdown = true; |
| return; |
| } |
| |
| var done int; |
| for done < len(appMsg) { |
| todo := len(appMsg); |
| if todo > maxTLSPlaintext { |
| todo = maxTLSPlaintext |
| } |
| w.writeRecord(&record{recordTypeApplicationData, w.major, w.minor, appMsg[done : done+todo]}); |
| done += todo; |
| } |
| } |