github.com/gdamore/mangos@v1.4.0/transport/tlstcp/tlstcp.go (about)

     1  // Copyright 2018 The Mangos Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use file except in compliance with the License.
     5  // You may obtain a copy of the license at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package tlstcp implements the TLS over TCP transport for mangos.
    16  package tlstcp
    17  
    18  import (
    19  	"crypto/tls"
    20  	"net"
    21  
    22  	"nanomsg.org/go-mangos"
    23  )
    24  
    25  type options map[string]interface{}
    26  
    27  func (o options) get(name string) (interface{}, error) {
    28  	if v, ok := o[name]; ok {
    29  		return v, nil
    30  	}
    31  	return nil, mangos.ErrBadOption
    32  }
    33  
    34  func (o options) set(name string, val interface{}) error {
    35  	switch name {
    36  	case mangos.OptionTLSConfig:
    37  		switch v := val.(type) {
    38  		case *tls.Config:
    39  			o[name] = v
    40  		default:
    41  			return mangos.ErrBadValue
    42  		}
    43  	default:
    44  		return mangos.ErrBadOption
    45  	}
    46  	return nil
    47  }
    48  
    49  func (o options) configTCP(conn *net.TCPConn) error {
    50  	if v, ok := o[mangos.OptionNoDelay]; ok {
    51  		if err := conn.SetNoDelay(v.(bool)); err != nil {
    52  			return err
    53  		}
    54  	}
    55  	if v, ok := o[mangos.OptionKeepAlive]; ok {
    56  		if err := conn.SetKeepAlive(v.(bool)); err != nil {
    57  			return err
    58  		}
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  func newOptions(t *tlsTran) options {
    65  	o := make(map[string]interface{})
    66  	o[mangos.OptionTLSConfig] = t.config
    67  	return options(o)
    68  }
    69  
    70  type dialer struct {
    71  	addr string
    72  	sock mangos.Socket
    73  	opts options
    74  }
    75  
    76  func (d *dialer) Dial() (_ mangos.Pipe, err error) {
    77  	var (
    78  		addr   *net.TCPAddr
    79  		config *tls.Config
    80  	)
    81  
    82  	if addr, err = mangos.ResolveTCPAddr(d.addr); err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	tconn, err := net.DialTCP("tcp", nil, addr)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	if err = d.opts.configTCP(tconn); err != nil {
    91  		tconn.Close()
    92  		return nil, err
    93  	}
    94  	if v, ok := d.opts[mangos.OptionTLSConfig]; ok {
    95  		config = v.(*tls.Config)
    96  	}
    97  	conn := tls.Client(tconn, config)
    98  	if err = conn.Handshake(); err != nil {
    99  		conn.Close()
   100  		return nil, err
   101  	}
   102  	return mangos.NewConnPipe(conn, d.sock,
   103  		mangos.PropTLSConnState, conn.ConnectionState())
   104  }
   105  
   106  func (d *dialer) SetOption(n string, v interface{}) error {
   107  	return d.opts.set(n, v)
   108  }
   109  
   110  func (d *dialer) GetOption(n string) (interface{}, error) {
   111  	return d.opts.get(n)
   112  }
   113  
   114  type listener struct {
   115  	sock     mangos.Socket
   116  	addr     *net.TCPAddr
   117  	bound    net.Addr
   118  	listener *net.TCPListener
   119  	opts     options
   120  	config   *tls.Config
   121  }
   122  
   123  func (l *listener) Listen() error {
   124  	var err error
   125  	v, ok := l.opts[mangos.OptionTLSConfig]
   126  	if !ok {
   127  		return mangos.ErrTLSNoConfig
   128  	}
   129  	l.config = v.(*tls.Config)
   130  	if l.config == nil {
   131  		return mangos.ErrTLSNoConfig
   132  	}
   133  	if l.config.Certificates == nil || len(l.config.Certificates) == 0 {
   134  		return mangos.ErrTLSNoCert
   135  	}
   136  
   137  	if l.listener, err = net.ListenTCP("tcp", l.addr); err != nil {
   138  		return err
   139  	}
   140  
   141  	l.bound = l.listener.Addr()
   142  
   143  	return nil
   144  }
   145  
   146  func (l *listener) Address() string {
   147  	if b := l.bound; b != nil {
   148  		return "tls+tcp://" + b.String()
   149  	}
   150  	return "tls+tcp://" + l.addr.String()
   151  }
   152  
   153  func (l *listener) Accept() (mangos.Pipe, error) {
   154  
   155  	tconn, err := l.listener.AcceptTCP()
   156  	if err != nil {
   157  		return nil, err
   158  	}
   159  
   160  	if err = l.opts.configTCP(tconn); err != nil {
   161  		tconn.Close()
   162  		return nil, err
   163  	}
   164  
   165  	conn := tls.Server(tconn, l.config)
   166  	if err = conn.Handshake(); err != nil {
   167  		conn.Close()
   168  		return nil, err
   169  	}
   170  	return mangos.NewConnPipe(conn, l.sock,
   171  		mangos.PropTLSConnState, conn.ConnectionState())
   172  }
   173  
   174  func (l *listener) Close() error {
   175  	l.listener.Close()
   176  	return nil
   177  }
   178  
   179  func (l *listener) SetOption(n string, v interface{}) error {
   180  	return l.opts.set(n, v)
   181  }
   182  
   183  func (l *listener) GetOption(n string) (interface{}, error) {
   184  	return l.opts.get(n)
   185  }
   186  
   187  type tlsTran struct {
   188  	config    *tls.Config
   189  	localAddr net.Addr
   190  }
   191  
   192  func (t *tlsTran) Scheme() string {
   193  	return "tls+tcp"
   194  }
   195  
   196  func (t *tlsTran) NewDialer(addr string, sock mangos.Socket) (mangos.PipeDialer, error) {
   197  	var err error
   198  
   199  	if addr, err = mangos.StripScheme(t, addr); err != nil {
   200  		return nil, err
   201  	}
   202  
   203  	// check to ensure the provided addr resolves correctly.
   204  	if _, err = mangos.ResolveTCPAddr(addr); err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	d := &dialer{sock: sock, opts: newOptions(t), addr: addr}
   209  
   210  	return d, nil
   211  }
   212  
   213  // NewAccepter implements the Transport NewAccepter method.
   214  func (t *tlsTran) NewListener(addr string, sock mangos.Socket) (mangos.PipeListener, error) {
   215  	var err error
   216  	l := &listener{sock: sock, opts: newOptions(t)}
   217  
   218  	if addr, err = mangos.StripScheme(t, addr); err != nil {
   219  		return nil, err
   220  	}
   221  	if l.addr, err = mangos.ResolveTCPAddr(addr); err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	return l, nil
   226  }
   227  
   228  // NewTransport allocates a new inproc transport.
   229  func NewTransport() mangos.Transport {
   230  	return &tlsTran{}
   231  }