blob: 7c76ddeb9b7487324820afa87ca4c3377b94a477 [file] [log] [blame]
// 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.
// This package partially implements the TLS 1.1 protocol, as specified in RFC 4346.
package tls
import (
"io"
"os"
"net"
"time"
)
// A Conn represents a secure connection.
type Conn struct {
net.Conn
writeChan chan<- []byte
readChan <-chan []byte
requestChan chan<- interface{}
readBuf []byte
eof bool
readTimeout, writeTimeout int64
}
func timeout(c chan<- bool, nsecs int64) {
time.Sleep(nsecs)
c <- true
}
func (tls *Conn) Read(p []byte) (int, os.Error) {
if len(tls.readBuf) == 0 {
if tls.eof {
return 0, os.EOF
}
var timeoutChan chan bool
if tls.readTimeout > 0 {
timeoutChan = make(chan bool)
go timeout(timeoutChan, tls.readTimeout)
}
select {
case b := <-tls.readChan:
tls.readBuf = b
case <-timeoutChan:
return 0, os.EAGAIN
}
// TLS distinguishes between orderly closes and truncations. An
// orderly close is represented by a zero length slice.
if closed(tls.readChan) {
return 0, io.ErrUnexpectedEOF
}
if len(tls.readBuf) == 0 {
tls.eof = true
return 0, os.EOF
}
}
n := copy(p, tls.readBuf)
tls.readBuf = tls.readBuf[n:]
return n, nil
}
func (tls *Conn) Write(p []byte) (int, os.Error) {
if tls.eof || closed(tls.readChan) {
return 0, os.EOF
}
var timeoutChan chan bool
if tls.writeTimeout > 0 {
timeoutChan = make(chan bool)
go timeout(timeoutChan, tls.writeTimeout)
}
select {
case tls.writeChan <- p:
case <-timeoutChan:
return 0, os.EAGAIN
}
return len(p), nil
}
func (tls *Conn) Close() os.Error {
close(tls.writeChan)
close(tls.requestChan)
tls.eof = true
return nil
}
func (tls *Conn) SetTimeout(nsec int64) os.Error {
tls.readTimeout = nsec
tls.writeTimeout = nsec
return nil
}
func (tls *Conn) SetReadTimeout(nsec int64) os.Error {
tls.readTimeout = nsec
return nil
}
func (tls *Conn) SetWriteTimeout(nsec int64) os.Error {
tls.writeTimeout = nsec
return nil
}
func (tls *Conn) GetConnectionState() ConnectionState {
replyChan := make(chan ConnectionState)
tls.requestChan <- getConnectionState{replyChan}
return <-replyChan
}
func (tls *Conn) WaitConnectionState() ConnectionState {
replyChan := make(chan ConnectionState)
tls.requestChan <- waitConnectionState{replyChan}
return <-replyChan
}
type handshaker interface {
loop(writeChan chan<- interface{}, controlChan chan<- interface{}, msgChan <-chan interface{}, config *Config)
}
// Server establishes a secure connection over the given connection and acts
// as a TLS server.
func startTLSGoroutines(conn net.Conn, h handshaker, config *Config) *Conn {
tls := new(Conn)
tls.Conn = conn
writeChan := make(chan []byte)
readChan := make(chan []byte)
requestChan := make(chan interface{})
tls.writeChan = writeChan
tls.readChan = readChan
tls.requestChan = requestChan
handshakeWriterChan := make(chan interface{})
processorHandshakeChan := make(chan interface{})
handshakeProcessorChan := make(chan interface{})
readerProcessorChan := make(chan *record)
go new(recordWriter).loop(conn, writeChan, handshakeWriterChan)
go recordReader(readerProcessorChan, conn)
go new(recordProcessor).loop(readChan, requestChan, handshakeProcessorChan, readerProcessorChan, processorHandshakeChan)
go h.loop(handshakeWriterChan, handshakeProcessorChan, processorHandshakeChan, config)
return tls
}
func Server(conn net.Conn, config *Config) *Conn {
return startTLSGoroutines(conn, new(serverHandshake), config)
}
func Client(conn net.Conn, config *Config) *Conn {
return startTLSGoroutines(conn, new(clientHandshake), config)
}
type Listener struct {
listener net.Listener
config *Config
}
func (l *Listener) Accept() (c net.Conn, err os.Error) {
c, err = l.listener.Accept()
if err != nil {
return
}
c = Server(c, l.config)
return
}
func (l *Listener) Close() os.Error { return l.listener.Close() }
func (l *Listener) Addr() net.Addr { return l.listener.Addr() }
// NewListener creates a Listener which accepts connections from an inner
// Listener and wraps each connection with Server.
func NewListener(listener net.Listener, config *Config) (l *Listener) {
l = new(Listener)
l.listener = listener
l.config = config
return
}