github.com/braveheart12/insolar-09-08-19@v0.8.7/network/transport/udp.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  	"bytes"
    22  	"context"
    23  	"fmt"
    24  	"io"
    25  	"net"
    26  
    27  	"github.com/insolar/insolar/consensus"
    28  	"github.com/insolar/insolar/consensus/packets"
    29  	"github.com/insolar/insolar/instrumentation/inslogger"
    30  	"github.com/insolar/insolar/log"
    31  	"github.com/insolar/insolar/network/transport/packet"
    32  	"github.com/insolar/insolar/network/transport/relay"
    33  	"github.com/insolar/insolar/network/utils"
    34  	"github.com/pkg/errors"
    35  	"go.opencensus.io/stats"
    36  )
    37  
    38  const udpMaxPacketSize = 1400
    39  
    40  type udpTransport struct {
    41  	baseTransport
    42  	conn    net.PacketConn
    43  	address string
    44  }
    45  
    46  type udpSerializer struct{}
    47  
    48  func (b *udpSerializer) SerializePacket(q *packet.Packet) ([]byte, error) {
    49  	data, ok := q.Data.(packets.ConsensusPacket)
    50  	if !ok {
    51  		return nil, errors.New("could not convert packet to ConsensusPacket type")
    52  	}
    53  	return data.Serialize()
    54  }
    55  
    56  func (b *udpSerializer) DeserializePacket(conn io.Reader) (*packet.Packet, error) {
    57  	data, err := packets.ExtractPacket(conn)
    58  	if err != nil {
    59  		return nil, errors.Wrap(err, "could not convert network datagram to ConsensusPacket")
    60  	}
    61  	p := &packet.Packet{}
    62  	p.Data = data
    63  	return p, nil
    64  }
    65  
    66  func newUDPTransport(conn net.PacketConn, proxy relay.Proxy, publicAddress string) (*udpTransport, error) {
    67  	transport := &udpTransport{baseTransport: newBaseTransport(proxy, publicAddress), conn: conn}
    68  	transport.sendFunc = transport.send
    69  	transport.serializer = &udpSerializer{}
    70  
    71  	return transport, nil
    72  }
    73  
    74  func (t *udpTransport) send(recvAddress string, data []byte) error {
    75  	log.Debug("Sending PURE_UDP request")
    76  	if len(data) > udpMaxPacketSize {
    77  		return errors.New(fmt.Sprintf("udpTransport.send: too big input data. Maximum: %d. Current: %d",
    78  			udpMaxPacketSize, len(data)))
    79  	}
    80  
    81  	// TODO: may be try to send second time if error
    82  	// TODO: skip resolving every time by caching result
    83  	udpAddr, err := net.ResolveUDPAddr("udp", recvAddress)
    84  	if err != nil {
    85  		return errors.Wrap(err, "udpTransport.send")
    86  	}
    87  
    88  	udpConn, err := net.DialUDP("udp", nil, udpAddr)
    89  	if err != nil {
    90  		return errors.Wrap(err, "udpTransport.send")
    91  	}
    92  	defer utils.CloseVerbose(udpConn)
    93  
    94  	log.Debug("udpTransport.send: len = ", len(data))
    95  	n, err := udpConn.Write(data)
    96  	stats.Record(context.Background(), consensus.SentSize.M(int64(n)))
    97  	return errors.Wrap(err, "Failed to write data")
    98  }
    99  
   100  func (t *udpTransport) prepareListen() (net.PacketConn, error) {
   101  	t.mutex.Lock()
   102  	defer t.mutex.Unlock()
   103  
   104  	t.disconnectStarted = make(chan bool, 1)
   105  	t.disconnectFinished = make(chan bool, 1)
   106  
   107  	if t.conn != nil {
   108  		t.address = t.conn.LocalAddr().String()
   109  	} else {
   110  		var err error
   111  		t.conn, err = net.ListenPacket("udp", t.address)
   112  		if err != nil {
   113  			return nil, errors.Wrap(err, "failed to listen UDP")
   114  		}
   115  	}
   116  
   117  	return t.conn, nil
   118  }
   119  
   120  // Start starts networking.
   121  func (t *udpTransport) Listen(ctx context.Context) error {
   122  	logger := inslogger.FromContext(ctx)
   123  	logger.Info("[ Listen ] Start UDP transport")
   124  
   125  	conn, err := t.prepareListen()
   126  	if err != nil {
   127  		logger.Infof("[ Listen ] Failed to prepare UDP transport: " + err.Error())
   128  		return err
   129  	}
   130  
   131  	go func() {
   132  		for {
   133  			buf := make([]byte, udpMaxPacketSize)
   134  			n, addr, err := conn.ReadFrom(buf)
   135  			if err != nil {
   136  				<-t.disconnectFinished
   137  				logger.Error("failed to read UDP: ", err.Error())
   138  				return // TODO: we probably shouldn't return here
   139  			}
   140  
   141  			stats.Record(ctx, consensus.RecvSize.M(int64(n)))
   142  			go t.handleAcceptedConnection(buf[:n], addr)
   143  		}
   144  	}()
   145  
   146  	return nil
   147  }
   148  
   149  // Stop stops networking.
   150  func (t *udpTransport) Stop() {
   151  	t.mutex.Lock()
   152  	defer t.mutex.Unlock()
   153  
   154  	log.Info("Stop UDP transport")
   155  	t.prepareDisconnect()
   156  
   157  	if t.conn != nil {
   158  		utils.CloseVerbose(t.conn)
   159  		t.conn = nil
   160  	}
   161  }
   162  
   163  func (t *udpTransport) handleAcceptedConnection(data []byte, addr net.Addr) {
   164  	r := bytes.NewReader(data)
   165  	msg, err := t.serializer.DeserializePacket(r)
   166  	if err != nil {
   167  		log.Error("[ handleAcceptedConnection ] ", err)
   168  		return
   169  	}
   170  	log.Debug("[ handleAcceptedConnection ] Packet processed. size: ", len(data), ". Address: ", addr)
   171  
   172  	go t.packetHandler.Handle(context.TODO(), msg)
   173  }