github.com/xmplusdev/xray-core@v1.8.10/transport/internet/grpc/encoding/hunkconn.go (about)

     1  package encoding
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"net"
     7  
     8  	"github.com/xmplusdev/xray-core/common/buf"
     9  	xnet "github.com/xmplusdev/xray-core/common/net"
    10  	"github.com/xmplusdev/xray-core/common/net/cnc"
    11  	"github.com/xmplusdev/xray-core/common/signal/done"
    12  	"google.golang.org/grpc/metadata"
    13  	"google.golang.org/grpc/peer"
    14  )
    15  
    16  type HunkConn interface {
    17  	Context() context.Context
    18  	Send(*Hunk) error
    19  	Recv() (*Hunk, error)
    20  	SendMsg(m interface{}) error
    21  	RecvMsg(m interface{}) error
    22  }
    23  
    24  type StreamCloser interface {
    25  	CloseSend() error
    26  }
    27  
    28  type HunkReaderWriter struct {
    29  	hc     HunkConn
    30  	cancel context.CancelFunc
    31  	done   *done.Instance
    32  
    33  	buf   []byte
    34  	index int
    35  }
    36  
    37  func NewHunkReadWriter(hc HunkConn, cancel context.CancelFunc) *HunkReaderWriter {
    38  	return &HunkReaderWriter{hc, cancel, done.New(), nil, 0}
    39  }
    40  
    41  func NewHunkConn(hc HunkConn, cancel context.CancelFunc) net.Conn {
    42  	var rAddr net.Addr
    43  	pr, ok := peer.FromContext(hc.Context())
    44  	if ok {
    45  		rAddr = pr.Addr
    46  	} else {
    47  		rAddr = &net.TCPAddr{
    48  			IP:   []byte{0, 0, 0, 0},
    49  			Port: 0,
    50  		}
    51  	}
    52  
    53  	md, ok := metadata.FromIncomingContext(hc.Context())
    54  	if ok {
    55  		header := md.Get("x-real-ip")
    56  		if len(header) > 0 {
    57  			realip := xnet.ParseAddress(header[0])
    58  			if realip.Family().IsIP() {
    59  				rAddr = &net.TCPAddr{
    60  					IP:   realip.IP(),
    61  					Port: 0,
    62  				}
    63  			}
    64  		}
    65  	}
    66  	wrc := NewHunkReadWriter(hc, cancel)
    67  	return cnc.NewConnection(
    68  		cnc.ConnectionInput(wrc),
    69  		cnc.ConnectionOutput(wrc),
    70  		cnc.ConnectionOnClose(wrc),
    71  		cnc.ConnectionRemoteAddr(rAddr),
    72  	)
    73  }
    74  
    75  func (h *HunkReaderWriter) forceFetch() error {
    76  	hunk, err := h.hc.Recv()
    77  	if err != nil {
    78  		if err == io.EOF {
    79  			return err
    80  		}
    81  
    82  		return newError("failed to fetch hunk from gRPC tunnel").Base(err)
    83  	}
    84  
    85  	h.buf = hunk.Data
    86  	h.index = 0
    87  
    88  	return nil
    89  }
    90  
    91  func (h *HunkReaderWriter) Read(buf []byte) (int, error) {
    92  	if h.done.Done() {
    93  		return 0, io.EOF
    94  	}
    95  
    96  	if h.index >= len(h.buf) {
    97  		if err := h.forceFetch(); err != nil {
    98  			return 0, err
    99  		}
   100  	}
   101  	n := copy(buf, h.buf[h.index:])
   102  	h.index += n
   103  
   104  	return n, nil
   105  }
   106  
   107  func (h *HunkReaderWriter) ReadMultiBuffer() (buf.MultiBuffer, error) {
   108  	if h.done.Done() {
   109  		return nil, io.EOF
   110  	}
   111  	if h.index >= len(h.buf) {
   112  		if err := h.forceFetch(); err != nil {
   113  			return nil, err
   114  		}
   115  	}
   116  
   117  	if cap(h.buf) >= buf.Size {
   118  		b := h.buf
   119  		h.index = len(h.buf)
   120  		return buf.MultiBuffer{buf.NewExisted(b)}, nil
   121  	}
   122  
   123  	b := buf.New()
   124  	_, err := b.ReadFrom(h)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  	return buf.MultiBuffer{b}, nil
   129  }
   130  
   131  func (h *HunkReaderWriter) Write(buf []byte) (int, error) {
   132  	if h.done.Done() {
   133  		return 0, io.ErrClosedPipe
   134  	}
   135  
   136  	err := h.hc.Send(&Hunk{Data: buf[:]})
   137  	if err != nil {
   138  		return 0, newError("failed to send data over gRPC tunnel").Base(err)
   139  	}
   140  	return len(buf), nil
   141  }
   142  
   143  func (h *HunkReaderWriter) Close() error {
   144  	if h.cancel != nil {
   145  		h.cancel()
   146  	}
   147  	if sc, match := h.hc.(StreamCloser); match {
   148  		return sc.CloseSend()
   149  	}
   150  
   151  	return h.done.Close()
   152  }