2018-06-26 16:15:48 +08:00
|
|
|
package conn
|
2017-07-13 21:55:41 +08:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2020-08-25 22:14:08 +08:00
|
|
|
"errors"
|
2017-07-13 21:55:41 +08:00
|
|
|
"io"
|
|
|
|
"net"
|
2020-08-25 22:14:08 +08:00
|
|
|
"os"
|
|
|
|
"sync"
|
2017-07-13 21:55:41 +08:00
|
|
|
"time"
|
2020-04-19 17:03:39 +08:00
|
|
|
|
|
|
|
"github.com/nadoo/glider/common/pool"
|
2017-07-13 21:55:41 +08:00
|
|
|
)
|
|
|
|
|
2020-04-19 17:03:39 +08:00
|
|
|
const (
|
2020-08-25 22:14:08 +08:00
|
|
|
// TCPBufSize is the size of tcp buffer.
|
2020-04-19 17:03:39 +08:00
|
|
|
TCPBufSize = 16 << 10
|
|
|
|
|
2020-08-25 22:14:08 +08:00
|
|
|
// UDPBufSize is the size of udp buffer.
|
2020-04-19 17:03:39 +08:00
|
|
|
UDPBufSize = 64 << 10
|
|
|
|
)
|
2018-06-26 16:15:48 +08:00
|
|
|
|
2020-08-25 22:14:08 +08:00
|
|
|
// Conn is a connection with buffered reader.
|
2018-06-26 16:15:48 +08:00
|
|
|
type Conn struct {
|
2017-07-13 21:55:41 +08:00
|
|
|
r *bufio.Reader
|
|
|
|
net.Conn
|
|
|
|
}
|
|
|
|
|
2019-03-18 23:37:01 +08:00
|
|
|
// NewConn returns a new conn.
|
2018-12-14 00:02:25 +08:00
|
|
|
func NewConn(c net.Conn) *Conn {
|
|
|
|
return &Conn{bufio.NewReader(c), c}
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
|
|
|
|
2019-03-18 23:37:01 +08:00
|
|
|
// Peek returns the next n bytes without advancing the reader.
|
2018-12-14 00:02:25 +08:00
|
|
|
func (c *Conn) Peek(n int) ([]byte, error) {
|
2017-07-13 21:55:41 +08:00
|
|
|
return c.r.Peek(n)
|
|
|
|
}
|
|
|
|
|
2018-12-14 00:02:25 +08:00
|
|
|
func (c *Conn) Read(p []byte) (int, error) {
|
2017-07-13 21:55:41 +08:00
|
|
|
return c.r.Read(p)
|
|
|
|
}
|
|
|
|
|
2019-03-18 23:37:01 +08:00
|
|
|
// Reader returns the internal bufio.Reader.
|
2018-12-14 00:02:25 +08:00
|
|
|
func (c *Conn) Reader() *bufio.Reader {
|
|
|
|
return c.r
|
|
|
|
}
|
|
|
|
|
2019-03-18 23:37:01 +08:00
|
|
|
// Relay relays between left and right.
|
2020-08-25 22:14:08 +08:00
|
|
|
func Relay(left, right net.Conn) error {
|
|
|
|
var err, err1 error
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
var wait = 5 * time.Second
|
2017-07-13 21:55:41 +08:00
|
|
|
|
2020-08-25 22:14:08 +08:00
|
|
|
wg.Add(1)
|
2017-07-13 21:55:41 +08:00
|
|
|
go func() {
|
2020-08-25 22:14:08 +08:00
|
|
|
defer wg.Done()
|
|
|
|
_, err1 = Copy(right, left)
|
|
|
|
right.SetReadDeadline(time.Now().Add(wait)) // unblock read on right
|
2017-07-13 21:55:41 +08:00
|
|
|
}()
|
|
|
|
|
2020-08-25 22:14:08 +08:00
|
|
|
_, err = Copy(left, right)
|
|
|
|
left.SetReadDeadline(time.Now().Add(wait)) // unblock read on left
|
|
|
|
wg.Wait()
|
2020-04-19 17:03:39 +08:00
|
|
|
|
2020-08-25 22:14:08 +08:00
|
|
|
if err1 != nil && !errors.Is(err1, os.ErrDeadlineExceeded) { // requires Go 1.15+
|
|
|
|
return err1
|
|
|
|
}
|
2017-07-13 21:55:41 +08:00
|
|
|
|
2020-08-25 22:14:08 +08:00
|
|
|
if err != nil && !errors.Is(err, os.ErrDeadlineExceeded) {
|
|
|
|
return err
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
2020-08-25 22:14:08 +08:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-09-03 00:12:00 +08:00
|
|
|
func worthReadFrom(src io.Reader) bool {
|
|
|
|
switch v := src.(type) {
|
|
|
|
case *net.TCPConn:
|
|
|
|
return true
|
|
|
|
case *net.UnixConn:
|
|
|
|
return true
|
|
|
|
case *os.File:
|
|
|
|
fi, err := v.Stat()
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return fi.Mode().IsRegular()
|
|
|
|
case *io.LimitedReader:
|
|
|
|
return worthReadFrom(v.R)
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-25 22:14:08 +08:00
|
|
|
// Copy copies from src to dst.
|
2020-09-03 00:12:00 +08:00
|
|
|
// it will try to avoid memory allocating by using WriteTo or ReadFrom method,
|
|
|
|
// if both failed, then it'll fallback to call CopyBuffer method.
|
2020-08-25 22:14:08 +08:00
|
|
|
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
2020-09-03 00:12:00 +08:00
|
|
|
if wt, ok := src.(io.WriterTo); ok {
|
|
|
|
return wt.WriteTo(dst)
|
|
|
|
}
|
|
|
|
|
|
|
|
if rt, ok := dst.(io.ReaderFrom); ok && worthReadFrom(src) {
|
|
|
|
return rt.ReadFrom(src)
|
|
|
|
}
|
|
|
|
|
|
|
|
return CopyBuffer(dst, src)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CopyBuffer copies from src to dst with a userspace buffer.
|
|
|
|
func CopyBuffer(dst io.Writer, src io.Reader) (written int64, err error) {
|
|
|
|
size := TCPBufSize
|
|
|
|
if l, ok := src.(*io.LimitedReader); ok && int64(size) > l.N {
|
|
|
|
if l.N < 1 {
|
|
|
|
size = 1
|
|
|
|
} else {
|
|
|
|
size = int(l.N)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
buf := pool.GetBuffer(size)
|
2020-08-25 22:14:08 +08:00
|
|
|
defer pool.PutBuffer(buf)
|
|
|
|
|
2020-09-03 00:12:00 +08:00
|
|
|
for {
|
|
|
|
nr, er := src.Read(buf)
|
|
|
|
if nr > 0 {
|
|
|
|
nw, ew := dst.Write(buf[0:nr])
|
|
|
|
if nw > 0 {
|
|
|
|
written += int64(nw)
|
|
|
|
}
|
|
|
|
if ew != nil {
|
|
|
|
err = ew
|
|
|
|
break
|
|
|
|
}
|
|
|
|
if nr != nw {
|
|
|
|
err = io.ErrShortWrite
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if er != nil {
|
|
|
|
if er != io.EOF {
|
|
|
|
err = er
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return written, err
|
2017-07-13 21:55:41 +08:00
|
|
|
}
|
2018-01-08 18:14:57 +08:00
|
|
|
|
2019-03-18 23:37:01 +08:00
|
|
|
// RelayUDP copys from src to dst at target with read timeout.
|
|
|
|
func RelayUDP(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration) error {
|
2020-04-19 23:20:15 +08:00
|
|
|
b := pool.GetBuffer(UDPBufSize)
|
|
|
|
defer pool.PutBuffer(b)
|
2020-04-19 17:03:39 +08:00
|
|
|
|
2018-01-08 18:14:57 +08:00
|
|
|
for {
|
|
|
|
src.SetReadDeadline(time.Now().Add(timeout))
|
2020-04-19 23:20:15 +08:00
|
|
|
n, _, err := src.ReadFrom(b)
|
2018-01-08 18:14:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-04-19 23:20:15 +08:00
|
|
|
_, err = dst.WriteTo(b[:n], target)
|
2018-01-08 18:14:57 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-01-13 13:20:15 +08:00
|
|
|
|
2019-03-18 23:37:01 +08:00
|
|
|
// OutboundIP returns preferred outbound ip of this machine.
|
2018-01-13 14:34:49 +08:00
|
|
|
func OutboundIP() string {
|
2018-01-13 13:20:15 +08:00
|
|
|
conn, err := net.Dial("udp", "8.8.8.8:80")
|
|
|
|
if err != nil {
|
2018-01-13 14:34:49 +08:00
|
|
|
return ""
|
2018-01-13 13:20:15 +08:00
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
|
2018-01-13 14:34:49 +08:00
|
|
|
return conn.LocalAddr().(*net.UDPAddr).IP.String()
|
2018-01-13 13:20:15 +08:00
|
|
|
}
|