// 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.listener = listener; l.config = config; return; }