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 }