// 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;
}
}
|