github.com/braveheart12/insolar-09-08-19@v0.8.7/network/transport/quic.go (about)

     1  /*
     2   * The Clear BSD License
     3   *
     4   * Copyright (c) 2019 Insolar Technologies
     5   *
     6   * All rights reserved.
     7   *
     8   * Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
     9   *
    10   *  Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    11   *  Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
    12   *  Neither the name of Insolar Technologies nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
    13   *
    14   * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    15   *
    16   */
    17  
    18  package transport
    19  
    20  import (
    21  	"context"
    22  	"crypto/rand"
    23  	"crypto/rsa"
    24  	"crypto/tls"
    25  	"crypto/x509"
    26  	"encoding/pem"
    27  	"math/big"
    28  	"net"
    29  
    30  	"github.com/lucas-clemente/quic-go"
    31  	"github.com/pkg/errors"
    32  
    33  	"github.com/insolar/insolar/log"
    34  	"github.com/insolar/insolar/network/transport/relay"
    35  	"github.com/insolar/insolar/network/utils"
    36  )
    37  
    38  type quicConnection struct {
    39  	session quic.Session
    40  	stream  quic.Stream
    41  }
    42  
    43  type quicTransport struct {
    44  	baseTransport
    45  	l           quic.Listener
    46  	conn        net.PacketConn
    47  	connections map[string]quicConnection
    48  }
    49  
    50  func NewQuicTransport(conn net.PacketConn, proxy relay.Proxy, publicAddress string) (Transport, error) {
    51  	listener, err := quic.Listen(conn, generateTLSConfig(), nil)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	transport := &quicTransport{
    57  		baseTransport: newBaseTransport(proxy, publicAddress),
    58  		l:             listener,
    59  		conn:          conn,
    60  		connections:   make(map[string]quicConnection),
    61  	}
    62  
    63  	transport.sendFunc = transport.send
    64  	return transport, nil
    65  }
    66  
    67  func (t *quicTransport) send(recvAddress string, data []byte) error {
    68  	conn, ok := t.connections[recvAddress]
    69  	var stream quic.Stream
    70  	var err error
    71  	if !ok {
    72  		var session quic.Session
    73  		session, stream, err = createConnection(recvAddress)
    74  		if err != nil {
    75  			return errors.Wrap(err, "[ send ] failed to create a connection")
    76  		}
    77  		t.connections[recvAddress] = quicConnection{session, stream}
    78  	} else {
    79  		stream = conn.stream
    80  	}
    81  
    82  	n, err := stream.Write(data)
    83  	if err != nil {
    84  		return errors.Wrap(err, "[ send ] failed to write to a stream")
    85  	}
    86  
    87  	if n != len(data) {
    88  		return errors.New("[ send ] sent a part of data")
    89  	}
    90  
    91  	return nil
    92  }
    93  
    94  // Start starts networking.
    95  func (t *quicTransport) Listen(ctx context.Context) error {
    96  	log.Debug("Start QUIC transport")
    97  
    98  	go func() {
    99  		for {
   100  			session, err := t.l.Accept()
   101  			if err != nil {
   102  				<-t.disconnectFinished
   103  				log.Error("failed to accept connection: ", err.Error())
   104  				return
   105  			}
   106  
   107  			log.Debug("accepted connection from ", session.RemoteAddr().String())
   108  			go t.handleAcceptedConnection(session)
   109  		}
   110  	}()
   111  
   112  	return nil
   113  }
   114  
   115  // Stop stops networking.
   116  func (t *quicTransport) Stop() {
   117  	t.mutex.Lock()
   118  	defer t.mutex.Unlock()
   119  
   120  	log.Debug("[ Stop ] Stop QUIC transport")
   121  	t.prepareDisconnect()
   122  
   123  	utils.CloseVerbose(t.l)
   124  
   125  	for _, conn := range t.connections {
   126  		utils.CloseVerbose(conn.stream)
   127  		utils.CloseVerbose(conn.session)
   128  	}
   129  
   130  	utils.CloseVerbose(t.conn)
   131  }
   132  
   133  func (t *quicTransport) handleAcceptedConnection(session quic.Session) {
   134  	stream, err := session.AcceptStream()
   135  	if err != nil {
   136  		log.Error(err, "[ handleAcceptedConnection ] failed to get a stream")
   137  	}
   138  
   139  	msg, err := t.serializer.DeserializePacket(stream)
   140  	if err != nil {
   141  		log.Error(err, "[ handleAcceptedConnection ] failed to deserialize a packet")
   142  	}
   143  
   144  	go t.packetHandler.Handle(context.TODO(), msg)
   145  
   146  	utils.CloseVerbose(stream)
   147  }
   148  
   149  func createConnection(addr string) (quic.Session, quic.Stream, error) {
   150  	// TODO: NETD18-78
   151  	session, err := quic.DialAddr(addr, &tls.Config{InsecureSkipVerify: true}, nil) //nolint: gosec
   152  	if err != nil {
   153  		return nil, nil, errors.Wrap(err, "[ createConnection ] failed to create a session")
   154  	}
   155  	stream, err := session.OpenStreamSync()
   156  	if err != nil {
   157  		return nil, nil, errors.Wrap(err, "[ createConnection ] failed to open a stream")
   158  	}
   159  	log.Debug("connected to: %s", session.RemoteAddr().String())
   160  	return session, stream, nil
   161  }
   162  
   163  // Setup a bare-bones TLS config for the server
   164  func generateTLSConfig() *tls.Config {
   165  	key, err := rsa.GenerateKey(rand.Reader, 2048)
   166  	if err != nil {
   167  		panic(err)
   168  	}
   169  	template := x509.Certificate{SerialNumber: big.NewInt(1)}
   170  	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
   171  	if err != nil {
   172  		panic(err)
   173  	}
   174  	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
   175  	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})
   176  
   177  	tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
   178  	if err != nil {
   179  		panic(err)
   180  	}
   181  	return &tls.Config{Certificates: []tls.Certificate{tlsCert}}
   182  }