2018-07-04 10:51:47 +00:00
|
|
|
package csms
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
|
2019-06-09 07:24:20 +00:00
|
|
|
"github.com/libp2p/go-libp2p-core/peer"
|
|
|
|
"github.com/libp2p/go-libp2p-core/sec"
|
2018-07-04 10:51:47 +00:00
|
|
|
mss "github.com/multiformats/go-multistream"
|
|
|
|
)
|
|
|
|
|
|
|
|
// SSMuxer is a multistream stream security transport multiplexer.
|
|
|
|
//
|
|
|
|
// SSMuxer is safe to use without initialization. However, it's not safe to move
|
|
|
|
// after use.
|
|
|
|
type SSMuxer struct {
|
|
|
|
mux mss.MultistreamMuxer
|
2019-06-09 07:24:20 +00:00
|
|
|
tpts map[string]sec.SecureTransport
|
2018-07-04 10:51:47 +00:00
|
|
|
OrderPreference []string
|
|
|
|
}
|
|
|
|
|
2019-06-09 07:24:20 +00:00
|
|
|
var _ sec.SecureTransport = (*SSMuxer)(nil)
|
2018-07-04 10:51:47 +00:00
|
|
|
|
|
|
|
// AddTransport adds a stream security transport to this multistream muxer.
|
|
|
|
//
|
|
|
|
// This method is *not* thread-safe. It should be called only when initializing
|
|
|
|
// the SSMuxer.
|
2019-06-09 07:24:20 +00:00
|
|
|
func (sm *SSMuxer) AddTransport(path string, transport sec.SecureTransport) {
|
2018-07-04 10:51:47 +00:00
|
|
|
if sm.tpts == nil {
|
2019-06-09 07:24:20 +00:00
|
|
|
sm.tpts = make(map[string]sec.SecureTransport, 1)
|
2018-07-04 10:51:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sm.mux.AddHandler(path, nil)
|
|
|
|
sm.tpts[path] = transport
|
|
|
|
sm.OrderPreference = append(sm.OrderPreference, path)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SecureInbound secures an inbound connection using this multistream
|
|
|
|
// multiplexed stream security transport.
|
2019-06-09 07:24:20 +00:00
|
|
|
func (sm *SSMuxer) SecureInbound(ctx context.Context, insecure net.Conn) (sec.SecureConn, error) {
|
2018-07-04 10:51:47 +00:00
|
|
|
tpt, err := sm.selectProto(ctx, insecure, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return tpt.SecureInbound(ctx, insecure)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SecureOutbound secures an outbound connection using this multistream
|
|
|
|
// multiplexed stream security transport.
|
2019-06-09 07:24:20 +00:00
|
|
|
func (sm *SSMuxer) SecureOutbound(ctx context.Context, insecure net.Conn, p peer.ID) (sec.SecureConn, error) {
|
2018-07-04 10:51:47 +00:00
|
|
|
tpt, err := sm.selectProto(ctx, insecure, false)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return tpt.SecureOutbound(ctx, insecure, p)
|
|
|
|
}
|
|
|
|
|
2019-06-09 07:24:20 +00:00
|
|
|
func (sm *SSMuxer) selectProto(ctx context.Context, insecure net.Conn, server bool) (sec.SecureTransport, error) {
|
2018-07-04 10:51:47 +00:00
|
|
|
var proto string
|
|
|
|
var err error
|
|
|
|
done := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer close(done)
|
|
|
|
if server {
|
|
|
|
proto, _, err = sm.mux.Negotiate(insecure)
|
|
|
|
} else {
|
|
|
|
proto, err = mss.SelectOneOf(sm.OrderPreference, insecure)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-done:
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if tpt, ok := sm.tpts[proto]; ok {
|
|
|
|
return tpt, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("selected unknown security transport")
|
|
|
|
case <-ctx.Done():
|
|
|
|
// We *must* do this. We have outstanding work on the connection
|
|
|
|
// and it's no longer safe to use.
|
|
|
|
insecure.Close()
|
2019-06-09 07:24:20 +00:00
|
|
|
<-done // wait to stop using the connection.
|
2018-07-04 10:51:47 +00:00
|
|
|
return nil, ctx.Err()
|
|
|
|
}
|
|
|
|
}
|