github.com/geph-official/geph2@v0.22.6-0.20210211030601-f527cb59b0df/libs/niaucchi4/e2e.go (about)

     1  package niaucchi4
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"encoding/hex"
     7  	"errors"
     8  	"fmt"
     9  	"log"
    10  	"net"
    11  	"time"
    12  
    13  	"github.com/ethereum/go-ethereum/rlp"
    14  	"github.com/patrickmn/go-cache"
    15  )
    16  
    17  // SessionAddr is a net.Addr that represents the ultimate counterparty in an e2e session.
    18  type SessionAddr [16]byte
    19  
    20  // NewSessAddr generates a new random sess addr
    21  func NewSessAddr() SessionAddr {
    22  	var k SessionAddr
    23  	rand.Read(k[:])
    24  	return k
    25  }
    26  
    27  func (sa SessionAddr) String() string {
    28  	return fmt.Sprintf("SA-%v", hex.EncodeToString(sa[:]))
    29  }
    30  
    31  // Network fulfills net.Addr
    32  func (sa SessionAddr) Network() string {
    33  	return "SESS"
    34  }
    35  
    36  type pcReadResult struct {
    37  	Contents []byte
    38  	Host     net.Addr
    39  }
    40  
    41  // E2EConn is a PacketConn implementing the E2E protocol.
    42  type E2EConn struct {
    43  	sidToSess *cache.Cache
    44  	readQueue chan pcReadResult
    45  	wire      net.PacketConn
    46  	pktbuf    [2048]byte
    47  	Closed    bool
    48  }
    49  
    50  // NewE2EConn creates a new e2e connection.
    51  func NewE2EConn(wire net.PacketConn) *E2EConn {
    52  	return &E2EConn{
    53  		sidToSess: cache.New(time.Hour, time.Hour),
    54  		readQueue: make(chan pcReadResult, 1024),
    55  		wire:      wire,
    56  	}
    57  }
    58  
    59  func (e2e *E2EConn) DebugInfo() [][]LinkInfo {
    60  	var tr [][]LinkInfo
    61  	for _, v := range e2e.sidToSess.Items() {
    62  		if !v.Expired() {
    63  			tr = append(tr, v.Object.(*e2eSession).DebugInfo())
    64  		}
    65  	}
    66  	return tr
    67  }
    68  
    69  func (e2e *E2EConn) genSendCallback() func(e2ePacket, net.Addr) {
    70  	return func(toSend e2ePacket, dest net.Addr) {
    71  		buffer := malloc(2048)
    72  		defer free(buffer)
    73  		buf := bytes.NewBuffer(buffer[:0])
    74  		err := rlp.Encode(buf, toSend)
    75  		if err != nil {
    76  			panic(err)
    77  		}
    78  		e2e.wire.WriteTo(buf.Bytes(), dest)
    79  	}
    80  }
    81  
    82  // SetSessPath is used by clients to have certain sessions go along certain paths.
    83  func (e2e *E2EConn) SetSessPath(sid SessionAddr, host net.Addr) {
    84  	var sess *e2eSession
    85  	if sessi, ok := e2e.sidToSess.Get(sid.String()); ok {
    86  		sess = sessi.(*e2eSession)
    87  	} else {
    88  		sess = newSession(sid, e2e.genSendCallback())
    89  	}
    90  	e2e.sidToSess.SetDefault(sid.String(), sess)
    91  	sess.AddPath(host)
    92  }
    93  
    94  // ReadFrom implements PacketConn.
    95  func (e2e *E2EConn) ReadFrom(p []byte) (n int, from net.Addr, err error) {
    96  	for {
    97  		select {
    98  		case prr := <-e2e.readQueue:
    99  			n = copy(p, prr.Contents)
   100  			from = prr.Host
   101  			return
   102  		default:
   103  			err = e2e.readOnePacket()
   104  			if err != nil {
   105  				return
   106  			}
   107  		}
   108  	}
   109  }
   110  
   111  // WriteTo implements PacketConn.
   112  func (e2e *E2EConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
   113  	sessid := addr.(SessionAddr)
   114  	sessi, ok := e2e.sidToSess.Get(sessid.String())
   115  	if !ok {
   116  		err = errors.New("session is dead")
   117  		return
   118  	}
   119  	sess := sessi.(*e2eSession)
   120  	e2e.sidToSess.SetDefault(sessid.String(), sess)
   121  	err = sess.Send(p)
   122  	if err != nil {
   123  		return
   124  	}
   125  	return
   126  }
   127  
   128  // UnderlyingLoss returns the underlying loss.
   129  func (e2e *E2EConn) UnderlyingLoss(destAddr net.Addr) (frac float64) {
   130  	sessid := destAddr.(SessionAddr)
   131  	sessi, ok := e2e.sidToSess.Get(sessid.String())
   132  	if !ok {
   133  		log.Println("cannot find underlying loss")
   134  		return
   135  	}
   136  	sess := sessi.(*e2eSession)
   137  	sess.lock.Lock()
   138  	defer sess.lock.Unlock()
   139  	rem := sess.info[sess.lastRemid]
   140  	frac = rem.remoteLoss
   141  	return
   142  }
   143  
   144  // Close closes the underlying socket.
   145  func (e2e *E2EConn) Close() error {
   146  	e2e.Closed = true
   147  	return e2e.wire.Close()
   148  }
   149  
   150  // LocalAddr returns the local address.
   151  func (e2e *E2EConn) LocalAddr() net.Addr {
   152  	return e2e.wire.LocalAddr()
   153  }
   154  
   155  // SetDeadline lala
   156  func (e2e *E2EConn) SetDeadline(t time.Time) error {
   157  	return e2e.wire.SetDeadline(t)
   158  }
   159  
   160  // SetWriteDeadline lala
   161  func (e2e *E2EConn) SetWriteDeadline(t time.Time) error {
   162  	return e2e.wire.SetDeadline(t)
   163  }
   164  
   165  // SetReadDeadline lala
   166  func (e2e *E2EConn) SetReadDeadline(t time.Time) error {
   167  	return e2e.wire.SetDeadline(t)
   168  }
   169  
   170  func (e2e *E2EConn) readOnePacket() error {
   171  	n, from, err := e2e.wire.ReadFrom(e2e.pktbuf[:])
   172  	if err != nil {
   173  		return err
   174  	}
   175  	var pkt e2ePacket
   176  	err = rlp.DecodeBytes(e2e.pktbuf[:n], &pkt)
   177  	if err != nil {
   178  		return err
   179  	}
   180  	var sess *e2eSession
   181  	if sessi, ok := e2e.sidToSess.Get(pkt.Session.String()); ok {
   182  		sess = sessi.(*e2eSession)
   183  	} else {
   184  		sess = newSession(pkt.Session, e2e.genSendCallback())
   185  	}
   186  	e2e.sidToSess.SetDefault(pkt.Session.String(), sess)
   187  	sess.AddPath(from)
   188  	sess.Input(pkt, from)
   189  	sess.FlushReadQueue(func(b []byte) {
   190  		select {
   191  		case e2e.readQueue <- pcReadResult{b, pkt.Session}:
   192  		default:
   193  			if doLogging {
   194  				log.Println("N4: dropping packet of size", len(b), "because overfull e2e.readQueue")
   195  			}
   196  		}
   197  	})
   198  	return nil
   199  }