diff --git a/conn.go b/common/conn/conn.go similarity index 66% rename from conn.go rename to common/conn/conn.go index e433b41..e33ca6e 100644 --- a/conn.go +++ b/common/conn/conn.go @@ -1,34 +1,38 @@ -package main +package conn import ( "bufio" "io" "net" "time" + + "github.com/nadoo/glider/common/log" ) -type conn struct { +const UDPBufSize = 65536 + +type Conn struct { r *bufio.Reader net.Conn } -func newConn(c net.Conn) conn { - return conn{bufio.NewReader(c), c} +func NewConn(c net.Conn) Conn { + return Conn{bufio.NewReader(c), c} } -func newConnSize(c net.Conn, n int) conn { - return conn{bufio.NewReaderSize(c, n), c} +func NewConnSize(c net.Conn, n int) Conn { + return Conn{bufio.NewReaderSize(c, n), c} } -func (c conn) Peek(n int) ([]byte, error) { +func (c Conn) Peek(n int) ([]byte, error) { return c.r.Peek(n) } -func (c conn) Read(p []byte) (int, error) { +func (c Conn) Read(p []byte) (int, error) { return c.r.Read(p) } -func relay(left, right net.Conn) (int64, int64, error) { +func Relay(left, right net.Conn) (int64, int64, error) { type res struct { N int64 Err error @@ -53,9 +57,9 @@ func relay(left, right net.Conn) (int64, int64, error) { return n, rs.N, err } -// copy from src to dst at target with read timeout -func timedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration) error { - buf := make([]byte, udpBufSize) +// TimedCopy copy from src to dst at target with read timeout +func TimedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout time.Duration) error { + buf := make([]byte, UDPBufSize) for { src.SetReadDeadline(time.Now().Add(timeout)) n, _, err := src.ReadFrom(buf) @@ -74,7 +78,7 @@ func timedCopy(dst net.PacketConn, target net.Addr, src net.PacketConn, timeout func OutboundIP() string { conn, err := net.Dial("udp", "8.8.8.8:80") if err != nil { - logf("get outbound ip error: %s", err) + log.F("get outbound ip error: %s", err) return "" } defer conn.Close() diff --git a/common/log/log.go b/common/log/log.go new file mode 100644 index 0000000..ae80f47 --- /dev/null +++ b/common/log/log.go @@ -0,0 +1,16 @@ +package log + +import "log" + +// Func defines a simple log function +type Func func(f string, v ...interface{}) + +var F Func + +func Fatal(v ...interface{}) { + log.Fatal(v) +} + +func Fatalf(f string, v ...interface{}) { + log.Fatalf(f, v) +} diff --git a/common/socks/socks.go b/common/socks/socks.go new file mode 100644 index 0000000..09f9515 --- /dev/null +++ b/common/socks/socks.go @@ -0,0 +1,173 @@ +package socks + +import ( + "errors" + "io" + "net" + "strconv" +) + +const ( + AuthNone = 0 + AuthPassword = 2 +) + +// SOCKS request commands as defined in RFC 1928 section 4. +const ( + CmdConnect = 1 + CmdBind = 2 + CmdUDPAssociate = 3 +) + +// SOCKS address types as defined in RFC 1928 section 5. +const ( + ATypeIP4 = 1 + ATypeDomain = 3 + ATypeIP6 = 4 +) + +// MaxAddrLen is the maximum size of SOCKS address in bytes. +const MaxAddrLen = 1 + 1 + 255 + 2 + +var Errors = []error{ + errors.New(""), + errors.New("general failure"), + errors.New("connection forbidden"), + errors.New("network unreachable"), + errors.New("host unreachable"), + errors.New("connection refused"), + errors.New("TTL expired"), + errors.New("command not supported"), + errors.New("address type not supported"), + errors.New("socks5UDPAssociate"), +} + +type Addr []byte + +// String serializes SOCKS address a to string form. +func (a Addr) String() string { + var host, port string + + switch ATYP(a[0]) { // address type + case ATypeDomain: + host = string(a[2 : 2+int(a[1])]) + port = strconv.Itoa((int(a[2+int(a[1])]) << 8) | int(a[2+int(a[1])+1])) + case ATypeIP4: + host = net.IP(a[1 : 1+net.IPv4len]).String() + port = strconv.Itoa((int(a[1+net.IPv4len]) << 8) | int(a[1+net.IPv4len+1])) + case ATypeIP6: + host = net.IP(a[1 : 1+net.IPv6len]).String() + port = strconv.Itoa((int(a[1+net.IPv6len]) << 8) | int(a[1+net.IPv6len+1])) + } + + return net.JoinHostPort(host, port) +} + +// UoT udp over tcp +func UoT(b byte) bool { + return b&0x8 == 0x8 +} + +// ATYP return the address type +func ATYP(b byte) int { + return int(b &^ 0x8) +} + +func ReadAddrBuf(r io.Reader, b []byte) (Addr, error) { + if len(b) < MaxAddrLen { + return nil, io.ErrShortBuffer + } + _, err := io.ReadFull(r, b[:1]) // read 1st byte for address type + if err != nil { + return nil, err + } + + switch ATYP(b[0]) { + case ATypeDomain: + _, err = io.ReadFull(r, b[1:2]) // read 2nd byte for domain length + if err != nil { + return nil, err + } + _, err = io.ReadFull(r, b[2:2+int(b[1])+2]) + return b[:1+1+int(b[1])+2], err + case ATypeIP4: + _, err = io.ReadFull(r, b[1:1+net.IPv4len+2]) + return b[:1+net.IPv4len+2], err + case ATypeIP6: + _, err = io.ReadFull(r, b[1:1+net.IPv6len+2]) + return b[:1+net.IPv6len+2], err + } + + return nil, Errors[8] +} + +// ReadAddr reads just enough bytes from r to get a valid Addr. +func ReadAddr(r io.Reader) (Addr, error) { + return ReadAddrBuf(r, make([]byte, MaxAddrLen)) +} + +// SplitAddr slices a SOCKS address from beginning of b. Returns nil if failed. +func SplitAddr(b []byte) Addr { + addrLen := 1 + if len(b) < addrLen { + return nil + } + + switch ATYP(b[0]) { + case ATypeDomain: + if len(b) < 2 { + return nil + } + addrLen = 1 + 1 + int(b[1]) + 2 + case ATypeIP4: + addrLen = 1 + net.IPv4len + 2 + case ATypeIP6: + addrLen = 1 + net.IPv6len + 2 + default: + return nil + + } + + if len(b) < addrLen { + return nil + } + + return b[:addrLen] +} + +// ParseAddr parses the address in string s. Returns nil if failed. +func ParseAddr(s string) Addr { + var addr Addr + host, port, err := net.SplitHostPort(s) + if err != nil { + return nil + } + if ip := net.ParseIP(host); ip != nil { + if ip4 := ip.To4(); ip4 != nil { + addr = make([]byte, 1+net.IPv4len+2) + addr[0] = ATypeIP4 + copy(addr[1:], ip4) + } else { + addr = make([]byte, 1+net.IPv6len+2) + addr[0] = ATypeIP6 + copy(addr[1:], ip) + } + } else { + if len(host) > 255 { + return nil + } + addr = make([]byte, 1+1+len(host)+2) + addr[0] = ATypeDomain + addr[1] = byte(len(host)) + copy(addr[2:], host) + } + + portnum, err := strconv.ParseUint(port, 10, 16) + if err != nil { + return nil + } + + addr[len(addr)-2], addr[len(addr)-1] = byte(portnum>>8), byte(portnum) + + return addr +} diff --git a/conf.go b/conf.go index b56f894..19ce152 100644 --- a/conf.go +++ b/conf.go @@ -157,7 +157,7 @@ func usage() { fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, "Available methods for ss:\n") - fmt.Fprintf(os.Stderr, " "+ListCipher()) + fmt.Fprintf(os.Stderr, " AEAD_AES_128_GCM AEAD_AES_192_GCM AEAD_AES_256_GCM AEAD_CHACHA20_POLY1305 AES-128-CFB AES-128-CTR AES-192-CFB AES-192-CTR AES-256-CFB AES-256-CTR CHACHA20-IETF XCHACHA20") fmt.Fprintf(os.Stderr, "\n") fmt.Fprintf(os.Stderr, " NOTE: chacha20-ietf-poly1305 = AEAD_CHACHA20_POLY1305\n") fmt.Fprintf(os.Stderr, "\n") diff --git a/dns.go b/dns.go index c26cfe7..97006ee 100644 --- a/dns.go +++ b/dns.go @@ -8,6 +8,9 @@ import ( "io" "net" "strings" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" ) // DNSHeaderLen is the length of dns msg header @@ -139,7 +142,7 @@ type DNSAnswerHandler func(Domain, ip string) error // DNS . type DNS struct { - dialer Dialer + dialer proxy.Dialer addr string Tunnel bool @@ -151,7 +154,7 @@ type DNS struct { } // NewDNS returns a dns forwarder. client[dns.udp] -> glider[tcp] -> forwarder[dns.tcp] -> remote dns addr -func NewDNS(addr, raddr string, dialer Dialer, tunnel bool) (*DNS, error) { +func NewDNS(addr, raddr string, dialer proxy.Dialer, tunnel bool) (*DNS, error) { s := &DNS{ dialer: dialer, addr: addr, @@ -175,25 +178,25 @@ func (s *DNS) ListenAndServe() { func (s *DNS) ListenAndServeUDP() { c, err := net.ListenPacket("udp", s.addr) if err != nil { - logf("proxy-dns failed to listen on %s, error: %v", s.addr, err) + log.F("proxy-dns failed to listen on %s, error: %v", s.addr, err) return } defer c.Close() - logf("proxy-dns listening UDP on %s", s.addr) + log.F("proxy-dns listening UDP on %s", s.addr) for { b := make([]byte, DNSUDPMaxLen) n, clientAddr, err := c.ReadFrom(b) if err != nil { - logf("proxy-dns local read error: %v", err) + log.F("proxy-dns local read error: %v", err) continue } reqLen := uint16(n) // TODO: check here if reqLen <= DNSHeaderLen+2 { - logf("proxy-dns not enough data") + log.F("proxy-dns not enough data") continue } @@ -201,13 +204,13 @@ func (s *DNS) ListenAndServeUDP() { go func() { _, respMsg, err := s.Exchange(reqLen, reqMsg, clientAddr.String()) if err != nil { - logf("proxy-dns error in exchange: %s", err) + log.F("proxy-dns error in exchange: %s", err) return } _, err = c.WriteTo(respMsg, clientAddr) if err != nil { - logf("proxy-dns error in local write: %s", err) + log.F("proxy-dns error in local write: %s", err) return } @@ -219,16 +222,16 @@ func (s *DNS) ListenAndServeUDP() { func (s *DNS) ListenAndServeTCP() { l, err := net.Listen("tcp", s.addr) if err != nil { - logf("proxy-dns-tcp error: %v", err) + log.F("proxy-dns-tcp error: %v", err) return } - logf("proxy-dns-tcp listening TCP on %s", s.addr) + log.F("proxy-dns-tcp listening TCP on %s", s.addr) for { c, err := l.Accept() if err != nil { - logf("proxy-dns-tcp error: failed to accept: %v", err) + log.F("proxy-dns-tcp error: failed to accept: %v", err) continue } go s.ServeTCP(c) @@ -245,35 +248,35 @@ func (s *DNS) ServeTCP(c net.Conn) { var reqLen uint16 if err := binary.Read(c, binary.BigEndian, &reqLen); err != nil { - logf("proxy-dns-tcp failed to get request length: %v", err) + log.F("proxy-dns-tcp failed to get request length: %v", err) return } // TODO: check here if reqLen <= DNSHeaderLen+2 { - logf("proxy-dns-tcp not enough data") + log.F("proxy-dns-tcp not enough data") return } reqMsg := make([]byte, reqLen) _, err := io.ReadFull(c, reqMsg) if err != nil { - logf("proxy-dns-tcp error in read reqMsg %s", err) + log.F("proxy-dns-tcp error in read reqMsg %s", err) return } respLen, respMsg, err := s.Exchange(reqLen, reqMsg, c.RemoteAddr().String()) if err != nil { - logf("proxy-dns-tcp error in exchange: %s", err) + log.F("proxy-dns-tcp error in exchange: %s", err) return } if err := binary.Write(c, binary.BigEndian, respLen); err != nil { - logf("proxy-dns-tcp error in local write respLen: %s", err) + log.F("proxy-dns-tcp error in local write respLen: %s", err) return } if err := binary.Write(c, binary.BigEndian, respMsg); err != nil { - logf("proxy-dns-tcp error in local write respMsg: %s", err) + log.F("proxy-dns-tcp error in local write respMsg: %s", err) return } } @@ -284,7 +287,7 @@ func (s *DNS) Exchange(reqLen uint16, reqMsg []byte, addr string) (respLen uint1 // fmt.Printf("\ndns req len %d:\n%s\n", reqLen, hex.Dump(reqMsg[:])) query, err := parseQuestion(reqMsg) if err != nil { - logf("proxy-dns error in parseQuestion reqMsg: %s", err) + log.F("proxy-dns error in parseQuestion reqMsg: %s", err) return } @@ -295,29 +298,29 @@ func (s *DNS) Exchange(reqLen uint16, reqMsg []byte, addr string) (respLen uint1 rc, err := s.dialer.NextDialer(query.QNAME+":53").Dial("tcp", dnsServer) if err != nil { - logf("proxy-dns failed to connect to server %v: %v", dnsServer, err) + log.F("proxy-dns failed to connect to server %v: %v", dnsServer, err) return } defer rc.Close() if err = binary.Write(rc, binary.BigEndian, reqLen); err != nil { - logf("proxy-dns failed to write req length: %v", err) + log.F("proxy-dns failed to write req length: %v", err) return } if err = binary.Write(rc, binary.BigEndian, reqMsg); err != nil { - logf("proxy-dns failed to write req message: %v", err) + log.F("proxy-dns failed to write req message: %v", err) return } if err = binary.Read(rc, binary.BigEndian, &respLen); err != nil { - logf("proxy-dns failed to read response length: %v", err) + log.F("proxy-dns failed to read response length: %v", err) return } respMsg = make([]byte, respLen) _, err = io.ReadFull(rc, respMsg) if err != nil { - logf("proxy-dns error in read respMsg %s\n", err) + log.F("proxy-dns error in read respMsg %s\n", err) return } @@ -326,7 +329,7 @@ func (s *DNS) Exchange(reqLen uint16, reqMsg []byte, addr string) (respLen uint1 var ip string respReq, err := parseQuestion(respMsg) if err != nil { - logf("proxy-dns error in parseQuestion respMsg: %s", err) + log.F("proxy-dns error in parseQuestion respMsg: %s", err) return } @@ -336,7 +339,7 @@ func (s *DNS) Exchange(reqLen uint16, reqMsg []byte, addr string) (respLen uint1 var answers []*DNSRR answers, err = parseAnswers(respMsg[respReq.Offset:]) if err != nil { - logf("proxy-dns error in parseAnswers: %s", err) + log.F("proxy-dns error in parseAnswers: %s", err) return } @@ -352,7 +355,7 @@ func (s *DNS) Exchange(reqLen uint16, reqMsg []byte, addr string) (respLen uint1 } - logf("proxy-dns %s <-> %s, type: %d, %s: %s", addr, dnsServer, query.QTYPE, query.QNAME, ip) + log.F("proxy-dns %s <-> %s, type: %d, %s: %s", addr, dnsServer, query.QTYPE, query.QNAME, ip) return } diff --git a/dnstun.go b/dnstun.go deleted file mode 100644 index 609d0d4..0000000 --- a/dnstun.go +++ /dev/null @@ -1,35 +0,0 @@ -// https://tools.ietf.org/html/rfc1035 - -package main - -// DNSTun struct -type DNSTun struct { - dialer Dialer - addr string - - raddr string - - dns *DNS - tcp *TCPTun -} - -// NewDNSTun returns a dns tunnel forwarder. -func NewDNSTun(addr, raddr string, dialer Dialer) (*DNSTun, error) { - s := &DNSTun{ - dialer: dialer, - addr: addr, - - raddr: raddr, - } - - s.dns, _ = NewDNS(addr, raddr, dialer, true) - - return s, nil -} - -// ListenAndServe . -func (s *DNSTun) ListenAndServe() { - if s.dns != nil { - go s.dns.ListenAndServe() - } -} diff --git a/features.go b/features.go new file mode 100644 index 0000000..3bcb351 --- /dev/null +++ b/features.go @@ -0,0 +1,12 @@ +package main + +import ( + _ "github.com/nadoo/glider/proxy/http" + _ "github.com/nadoo/glider/proxy/mixed" + _ "github.com/nadoo/glider/proxy/socks5" + _ "github.com/nadoo/glider/proxy/ss" + _ "github.com/nadoo/glider/proxy/ssr" + _ "github.com/nadoo/glider/proxy/tcptun" + _ "github.com/nadoo/glider/proxy/udptun" + _ "github.com/nadoo/glider/proxy/uottun" +) diff --git a/features_linux.go b/features_linux.go new file mode 100644 index 0000000..636e616 --- /dev/null +++ b/features_linux.go @@ -0,0 +1,5 @@ +package main + +import ( + _ "github.com/nadoo/glider/proxy/redir" +) diff --git a/ipset_linux.go b/ipset_linux.go index eddb5e0..67e9222 100644 --- a/ipset_linux.go +++ b/ipset_linux.go @@ -7,13 +7,14 @@ package main import ( "bytes" "encoding/binary" - "log" "net" "strings" "sync" "sync/atomic" "syscall" "unsafe" + + "github.com/nadoo/glider/common/log" ) // netfilter netlink message types @@ -69,7 +70,7 @@ type IPSetManager struct { func NewIPSetManager(mainSet string, rules []*RuleConf) (*IPSetManager, error) { fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_NETFILTER) if err != nil { - logf("%s", err) + log.F("%s", err) return nil, err } // defer syscall.Close(fd) @@ -79,7 +80,7 @@ func NewIPSetManager(mainSet string, rules []*RuleConf) (*IPSetManager, error) { } if err = syscall.Bind(fd, &lsa); err != nil { - logf("%s", err) + log.F("%s", err) return nil, err } @@ -144,7 +145,7 @@ func CreateSet(fd int, lsa syscall.SockaddrNetlink, setName string) { log.Fatal("ipset: name too long") } - logf("ipset create %s hash:net", setName) + log.F("ipset create %s hash:net", setName) req := NewNetlinkRequest(IPSET_CMD_CREATE|(NFNL_SUBSYS_IPSET<<8), syscall.NLM_F_REQUEST) @@ -172,14 +173,14 @@ func CreateSet(fd int, lsa syscall.SockaddrNetlink, setName string) { err := syscall.Sendto(fd, req.Serialize(), 0, &lsa) if err != nil { - logf("%s", err) + log.F("%s", err) } FlushSet(fd, lsa, setName) } func FlushSet(fd int, lsa syscall.SockaddrNetlink, setName string) { - logf("ipset flush %s", setName) + log.F("ipset flush %s", setName) req := NewNetlinkRequest(IPSET_CMD_FLUSH|(NFNL_SUBSYS_IPSET<<8), syscall.NLM_F_REQUEST) @@ -190,7 +191,7 @@ func FlushSet(fd int, lsa syscall.SockaddrNetlink, setName string) { err := syscall.Sendto(fd, req.Serialize(), 0, &lsa) if err != nil { - logf("%s", err) + log.F("%s", err) } } @@ -201,10 +202,10 @@ func AddToSet(fd int, lsa syscall.SockaddrNetlink, setName, entry string) { } if len(setName) > IPSET_MAXNAMELEN { - logf("ipset: name too long") + log.F("ipset: name too long") } - logf("ipset add %s %s", setName, entry) + log.F("ipset add %s %s", setName, entry) var ip net.IP var cidr *net.IPNet @@ -215,7 +216,7 @@ func AddToSet(fd int, lsa syscall.SockaddrNetlink, setName, entry string) { } if ip == nil { - logf("ipset: parse %s error", entry) + log.F("ipset: parse %s error", entry) return } @@ -248,7 +249,7 @@ func AddToSet(fd int, lsa syscall.SockaddrNetlink, setName, entry string) { err = syscall.Sendto(fd, req.Serialize(), 0, &lsa) if err != nil { - logf("%s", err) + log.F("%s", err) } } diff --git a/log.go b/log.go deleted file mode 100644 index dd88acc..0000000 --- a/log.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import "log" - -// LogFunc defines a simple log function -type LogFunc func(f string, v ...interface{}) - -var logf LogFunc - -func init() { - logf = func(f string, v ...interface{}) { - if conf.Verbose { - log.Printf(f, v...) - } - } -} diff --git a/main.go b/main.go index 5c80aea..6fe1c33 100644 --- a/main.go +++ b/main.go @@ -1,24 +1,27 @@ package main import ( - "log" + stdlog "log" "os" "os/signal" "strings" "syscall" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" ) // VERSION . -const VERSION = "0.5.2" +const VERSION = "0.6.0" -func dialerFromConf() Dialer { +func dialerFromConf() proxy.Dialer { // global forwarders in xx.conf - var fwdrs []Dialer + var fwdrs []proxy.Dialer for _, chain := range conf.Forward { - var fwdr Dialer + var fwdr proxy.Dialer var err error for _, url := range strings.Split(chain, ",") { - fwdr, err = DialerFromURL(url, fwdr) + fwdr, err = proxy.DialerFromURL(url, fwdr) if err != nil { log.Fatal(err) } @@ -32,10 +35,17 @@ func dialerFromConf() Dialer { func main() { confInit() + + log.F = func(f string, v ...interface{}) { + if conf.Verbose { + stdlog.Printf(f, v...) + } + } + sDialer := NewRuleDialer(conf.rules, dialerFromConf()) for _, listen := range conf.Listen { - local, err := ServerFromURL(listen, sDialer) + local, err := proxy.ServerFromURL(listen, sDialer) if err != nil { log.Fatal(err) } @@ -45,7 +55,7 @@ func main() { ipsetM, err := NewIPSetManager(conf.IPSet, conf.rules) if err != nil { - logf("create ipset manager error: %s", err) + log.F("create ipset manager error: %s", err) } if conf.DNS != "" { diff --git a/mixed.go b/mixed.go deleted file mode 100644 index fc29de6..0000000 --- a/mixed.go +++ /dev/null @@ -1,105 +0,0 @@ -package main - -import ( - "bytes" - "net" -) - -// https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase. -var httpMethods = [...][]byte{ - []byte("GET"), - []byte("POST"), - []byte("PUT"), - []byte("DELETE"), - []byte("CONNECT"), - []byte("HEAD"), - []byte("OPTIONS"), - []byte("TRACE"), -} - -// MixedProxy struct -type MixedProxy struct { - dialer Dialer - addr string - - http *HTTP - socks5 *SOCKS5 -} - -// NewMixedProxy returns a mixed proxy. -func NewMixedProxy(addr, user, pass, rawQuery string, dialer Dialer) (*MixedProxy, error) { - p := &MixedProxy{ - dialer: dialer, - addr: addr, - } - - p.http, _ = NewHTTP(addr, user, pass, rawQuery, dialer) - p.socks5, _ = NewSOCKS5(addr, user, pass, dialer) - - return p, nil -} - -// ListenAndServe . -func (p *MixedProxy) ListenAndServe() { - - go p.socks5.ListenAndServeUDP() - - l, err := net.Listen("tcp", p.addr) - if err != nil { - logf("proxy-mixed failed to listen on %s: %v", p.addr, err) - return - } - - logf("proxy-mixed listening TCP on %s", p.addr) - - for { - c, err := l.Accept() - if err != nil { - logf("proxy-mixed failed to accept: %v", err) - continue - } - - go p.Serve(c) - } -} - -// Serve . -func (p *MixedProxy) Serve(conn net.Conn) { - defer conn.Close() - - if c, ok := conn.(*net.TCPConn); ok { - c.SetKeepAlive(true) - } - - c := newConn(conn) - - if p.socks5 != nil { - head, err := c.Peek(1) - if err != nil { - logf("proxy-mixed peek error: %s", err) - return - } - - // check socks5, client send socksversion: 5 as the first byte - if head[0] == socks5Version { - p.socks5.ServeTCP(c) - return - } - } - - if p.http != nil { - head, err := c.Peek(8) - if err != nil { - logf("proxy-mixed peek error: %s", err) - return - } - - for _, method := range httpMethods { - if bytes.HasPrefix(head, method) { - p.http.Serve(c) - return - } - } - } - -} diff --git a/dialer.go b/proxy/dialer.go similarity index 54% rename from dialer.go rename to proxy/dialer.go index 6721ef7..b25b3d1 100644 --- a/dialer.go +++ b/proxy/dialer.go @@ -1,12 +1,15 @@ -package main +package proxy import ( "errors" "net" "net/url" + "strings" + + "github.com/nadoo/glider/common/log" ) -// A Dialer means to establish a connection and relay it. +// A proxy.Dialer means to establish a connection and relay it. type Dialer interface { // Addr() Addr() string @@ -21,35 +24,30 @@ type Dialer interface { NextDialer(dstAddr string) Dialer } -// DialerFromURL parses url and get a Proxy -// TODO: table +type DialerCreator func(s string, dialer Dialer) (Dialer, error) + +var ( + dialerMap = make(map[string]DialerCreator) +) + +func RegisterDialer(name string, c DialerCreator) { + dialerMap[name] = c +} + func DialerFromURL(s string, dialer Dialer) (Dialer, error) { u, err := url.Parse(s) if err != nil { - logf("parse err: %s", err) + log.F("parse err: %s", err) return nil, err } - addr := u.Host - var user, pass string - if u.User != nil { - user = u.User.Username() - pass, _ = u.User.Password() - } - if dialer == nil { dialer = Direct } - switch u.Scheme { - case "http": - return NewHTTP(addr, user, pass, "", dialer) - case "socks5": - return NewSOCKS5(addr, user, pass, dialer) - case "ss": - return NewSS(addr, user, pass, dialer) - case "ssr": - return NewSSR(addr, user, pass, u.RawQuery, dialer) + c, ok := dialerMap[strings.ToLower(u.Scheme)] + if ok { + return c(s, dialer) } return nil, errors.New("unknown scheme '" + u.Scheme + "'") diff --git a/direct.go b/proxy/direct.go similarity index 89% rename from direct.go rename to proxy/direct.go index 576d06a..2652e7a 100644 --- a/direct.go +++ b/proxy/direct.go @@ -1,7 +1,9 @@ -package main +package proxy import ( "net" + + "github.com/nadoo/glider/common/log" ) // direct proxy @@ -33,7 +35,7 @@ func (d *direct) Dial(network, addr string) (net.Conn, error) { func (d *direct) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { pc, err := net.ListenPacket(network, "") if err != nil { - logf("ListenPacket error: %s", err) + log.F("ListenPacket error: %s", err) return nil, nil, err } diff --git a/http.go b/proxy/http/http.go similarity index 72% rename from http.go rename to proxy/http/http.go index 69fedd7..4ef64ed 100644 --- a/http.go +++ b/proxy/http/http.go @@ -1,7 +1,7 @@ // http proxy // NOTE: never keep-alive so the implementation can be much easier. -package main +package http import ( "bufio" @@ -15,61 +15,75 @@ import ( "net/url" "strings" "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" ) // HTTP struct type HTTP struct { - dialer Dialer + dialer proxy.Dialer addr string user string password string - xff bool // X-Forwarded-For - xsi bool // X-Server-IP - selfip string +} + +func init() { + proxy.RegisterDialer("http", NewHTTPDialer) + proxy.RegisterServer("http", NewHTTPServer) } // NewHTTP returns a http proxy. -func NewHTTP(addr, user, pass, rawQuery string, dialer Dialer) (*HTTP, error) { - s := &HTTP{ +func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + addr := u.Host + var user, pass string + if u.User != nil { + user = u.User.Username() + pass, _ = u.User.Password() + } + + h := &HTTP{ dialer: dialer, addr: addr, user: user, password: pass, - xff: false, - selfip: OutboundIP(), } - p, _ := url.ParseQuery(rawQuery) - if v, ok := p["xff"]; ok { - if v[0] == "true" { - s.xff = true - } - } + return h, nil +} - if v, ok := p["xsi"]; ok { - if v[0] == "true" { - s.xsi = true - } - } +// NewHTTPDialer returns a http proxy dialer. +func NewHTTPDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewHTTP(s, dialer) +} - return s, nil +// NewHTTPServer returns a http proxy server. +func NewHTTPServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewHTTP(s, dialer) } // ListenAndServe . func (s *HTTP) ListenAndServe() { l, err := net.Listen("tcp", s.addr) if err != nil { - logf("failed to listen on %s: %v", s.addr, err) + log.F("failed to listen on %s: %v", s.addr, err) return } defer l.Close() - logf("listening TCP on %s", s.addr) + log.F("listening TCP on %s", s.addr) for { c, err := l.Accept() if err != nil { - logf("proxy-http failed to accept: %v", err) + log.F("proxy-http failed to accept: %v", err) continue } @@ -99,7 +113,7 @@ func (s *HTTP) Serve(c net.Conn) { reqHeader, err := reqTP.ReadMIMEHeader() if err != nil { - logf("read header error:%s", err) + log.F("read header error:%s", err) return } cleanHeaders(reqHeader) @@ -107,18 +121,18 @@ func (s *HTTP) Serve(c net.Conn) { reqHeader.Set("Connection", "close") // X-Forwarded-For - if s.xff { - if reqHeader.Get("X-Forwarded-For") != "" { - reqHeader.Add("X-Forwarded-For", ",") - } - reqHeader.Add("X-Forwarded-For", c.RemoteAddr().(*net.TCPAddr).IP.String()) - reqHeader.Add("X-Forwarded-For", ",") - reqHeader.Add("X-Forwarded-For", s.selfip) - } + // if s.xff { + // if reqHeader.Get("X-Forwarded-For") != "" { + // reqHeader.Add("X-Forwarded-For", ",") + // } + // reqHeader.Add("X-Forwarded-For", c.RemoteAddr().(*net.TCPAddr).IP.String()) + // reqHeader.Add("X-Forwarded-For", ",") + // reqHeader.Add("X-Forwarded-For", s.selfip) + // } url, err := url.ParseRequestURI(requestURI) if err != nil { - logf("proxy-http parse request url error: %s", err) + log.F("proxy-http parse request url error: %s", err) return } @@ -130,7 +144,7 @@ func (s *HTTP) Serve(c net.Conn) { rc, err := s.dialer.Dial("tcp", tgt) if err != nil { fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto) - logf("proxy-http failed to dial: %v", err) + log.F("proxy-http failed to dial: %v", err) return } defer rc.Close() @@ -166,22 +180,18 @@ func (s *HTTP) Serve(c net.Conn) { respHeader, err := respTP.ReadMIMEHeader() if err != nil { - logf("proxy-http read header error:%s", err) + log.F("proxy-http read header error:%s", err) return } respHeader.Set("Proxy-Connection", "close") respHeader.Set("Connection", "close") - if s.xsi { - respHeader.Set("X-Server-IP", rc.RemoteAddr().(*net.TCPAddr).IP.String()) - } - var respBuf bytes.Buffer writeFirstLine(proto, code, status, &respBuf) writeHeaders(respHeader, &respBuf) - logf("proxy-http %s <-> %s", c.RemoteAddr(), tgt) + log.F("proxy-http %s <-> %s", c.RemoteAddr(), tgt) c.Write(respBuf.Bytes()) io.Copy(c, respR) @@ -193,20 +203,20 @@ func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) { if err != nil { c.Write([]byte(proto)) c.Write([]byte(" 502 ERROR\r\n\r\n")) - logf("proxy-http failed to dial: %v", err) + log.F("proxy-http failed to dial: %v", err) return } c.Write([]byte("HTTP/1.0 200 Connection established\r\n\r\n")) - logf("proxy-http %s <-> %s [c]", c.RemoteAddr(), requestURI) + log.F("proxy-http %s <-> %s [c]", c.RemoteAddr(), requestURI) - _, _, err = relay(c, rc) + _, _, err = conn.Relay(c, rc) if err != nil { if err, ok := err.(net.Error); ok && err.Timeout() { return // ignore i/o timeout } - logf("relay error: %v", err) + log.F("relay error: %v", err) } } @@ -214,13 +224,13 @@ func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) { func (s *HTTP) Addr() string { return s.addr } // NextDialer returns the next dialer -func (s *HTTP) NextDialer(dstAddr string) Dialer { return s.dialer.NextDialer(dstAddr) } +func (s *HTTP) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } // Dial connects to the address addr on the network net via the proxy. func (s *HTTP) Dial(network, addr string) (net.Conn, error) { rc, err := s.dialer.Dial(network, s.addr) if err != nil { - logf("proxy-http dial to %s error: %s", s.addr, err) + log.F("proxy-http dial to %s error: %s", s.addr, err) return nil, err } @@ -245,9 +255,9 @@ func (s *HTTP) Dial(network, addr string) (net.Conn, error) { if ok && code == "200" { return rc, err } else if code == "407" { - logf("proxy-http authencation needed by proxy %s", s.addr) + log.F("proxy-http authencation needed by proxy %s", s.addr) } else if code == "405" { - logf("proxy-http 'CONNECT' method not allowed by proxy %s", s.addr) + log.F("proxy-http 'CONNECT' method not allowed by proxy %s", s.addr) } return nil, errors.New("proxy-http cound not connect remote address: " + addr + ". error code: " + code) @@ -261,9 +271,9 @@ func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Add // parseFirstLine parses "GET /foo HTTP/1.1" OR "HTTP/1.1 200 OK" into its three parts. func parseFirstLine(tp *textproto.Reader) (r1, r2, r3 string, ok bool) { line, err := tp.ReadLine() - // logf("first line: %s", line) + // log.F("first line: %s", line) if err != nil { - logf("proxy-http read first line error:%s", err) + log.F("proxy-http read first line error:%s", err) return } diff --git a/proxy/mixed/mixed.go b/proxy/mixed/mixed.go new file mode 100644 index 0000000..f6f17d8 --- /dev/null +++ b/proxy/mixed/mixed.go @@ -0,0 +1,127 @@ +package mixed + +import ( + "bytes" + "net" + "net/url" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" + "github.com/nadoo/glider/proxy/http" + "github.com/nadoo/glider/proxy/socks5" +) + +// https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase. +var httpMethods = [...][]byte{ + []byte("GET"), + []byte("POST"), + []byte("PUT"), + []byte("DELETE"), + []byte("CONNECT"), + []byte("HEAD"), + []byte("OPTIONS"), + []byte("TRACE"), +} + +// MixedProxy struct +type MixedProxy struct { + dialer proxy.Dialer + addr string + + http *http.HTTP + socks5 *socks5.SOCKS5 +} + +func init() { + proxy.RegisterServer("mixed", NewMixedProxyServer) +} + +// NewMixedProxy returns a mixed proxy. +func NewMixedProxy(s string, dialer proxy.Dialer) (*MixedProxy, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + p := &MixedProxy{ + dialer: dialer, + addr: u.Host, + } + + p.http, _ = http.NewHTTP(s, dialer) + p.socks5, _ = socks5.NewSOCKS5(s, dialer) + + return p, nil +} + +// NewMixedProxyServer returns a mixed proxy server. +func NewMixedProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewMixedProxy(s, dialer) +} + +// ListenAndServe . +func (p *MixedProxy) ListenAndServe() { + + go p.socks5.ListenAndServeUDP() + + l, err := net.Listen("tcp", p.addr) + if err != nil { + log.F("proxy-mixed failed to listen on %s: %v", p.addr, err) + return + } + + log.F("proxy-mixed listening TCP on %s", p.addr) + + for { + c, err := l.Accept() + if err != nil { + log.F("proxy-mixed failed to accept: %v", err) + continue + } + + go p.Serve(c) + } +} + +// Serve . +func (p *MixedProxy) Serve(c net.Conn) { + defer c.Close() + + if c, ok := c.(*net.TCPConn); ok { + c.SetKeepAlive(true) + } + + cc := conn.NewConn(c) + + if p.socks5 != nil { + head, err := cc.Peek(1) + if err != nil { + log.F("proxy-mixed peek error: %s", err) + return + } + + // check socks5, client send socksversion: 5 as the first byte + if head[0] == socks5.Version { + p.socks5.ServeTCP(cc) + return + } + } + + if p.http != nil { + head, err := cc.Peek(8) + if err != nil { + log.F("proxy-mixed peek error: %s", err) + return + } + + for _, method := range httpMethods { + if bytes.HasPrefix(head, method) { + p.http.Serve(cc) + return + } + } + } + +} diff --git a/redir_linux.go b/proxy/redir/redir_linux.go similarity index 69% rename from redir_linux.go rename to proxy/redir/redir_linux.go index 0b2c6d8..a0e971a 100644 --- a/redir_linux.go +++ b/proxy/redir/redir_linux.go @@ -1,13 +1,19 @@ // getOrigDst: // https://github.com/shadowsocks/go-shadowsocks2/blob/master/tcp_linux.go#L30 -package main +package redir import ( "errors" "net" + "net/url" "syscall" "unsafe" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/common/socks" + "github.com/nadoo/glider/proxy" ) const ( @@ -19,34 +25,50 @@ const ( // RedirProxy struct type RedirProxy struct { - dialer Dialer + dialer proxy.Dialer addr string } +func init() { + proxy.RegisterServer("redir", NewRedirServer) +} + // NewRedirProxy returns a redirect proxy. -func NewRedirProxy(addr string, dialer Dialer) (*RedirProxy, error) { - s := &RedirProxy{ +func NewRedirProxy(s string, dialer proxy.Dialer) (*RedirProxy, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + addr := u.Host + r := &RedirProxy{ dialer: dialer, addr: addr, } - return s, nil + return r, nil +} + +// NewRedirServer returns a redir server. +func NewRedirServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewRedirProxy(s, dialer) } // ListenAndServe . func (s *RedirProxy) ListenAndServe() { l, err := net.Listen("tcp", s.addr) if err != nil { - logf("proxy-redir failed to listen on %s: %v", s.addr, err) + log.F("proxy-redir failed to listen on %s: %v", s.addr, err) return } - logf("proxy-redir listening TCP on %s", s.addr) + log.F("proxy-redir listening TCP on %s", s.addr) for { c, err := l.Accept() if err != nil { - logf("proxy-redir failed to accept: %v", err) + log.F("proxy-redir failed to accept: %v", err) continue } @@ -59,25 +81,25 @@ func (s *RedirProxy) ListenAndServe() { tgt, err := getOrigDst(c, false) if err != nil { - logf("proxy-redir failed to get target address: %v", err) + log.F("proxy-redir failed to get target address: %v", err) return } rc, err := s.dialer.Dial("tcp", tgt.String()) if err != nil { - logf("proxy-redir failed to connect to target: %v", err) + log.F("proxy-redir failed to connect to target: %v", err) return } defer rc.Close() - logf("proxy-redir %s <-> %s", c.RemoteAddr(), tgt) + log.F("proxy-redir %s <-> %s", c.RemoteAddr(), tgt) - _, _, err = relay(c, rc) + _, _, err = conn.Relay(c, rc) if err != nil { if err, ok := err.(net.Error); ok && err.Timeout() { return // ignore i/o timeout } - logf("proxy-redir relay error: %v", err) + log.F("proxy-redir relay error: %v", err) } }() @@ -85,7 +107,7 @@ func (s *RedirProxy) ListenAndServe() { } // Get the original destination of a TCP connection. -func getOrigDst(conn net.Conn, ipv6 bool) (Addr, error) { +func getOrigDst(conn net.Conn, ipv6 bool) (socks.Addr, error) { c, ok := conn.(*net.TCPConn) if !ok { return nil, errors.New("only work with TCP connection") @@ -113,7 +135,7 @@ func getOrigDst(conn net.Conn, ipv6 bool) (Addr, error) { } // Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c -func getorigdst(fd uintptr) (Addr, error) { +func getorigdst(fd uintptr) (socks.Addr, error) { raw := syscall.RawSockaddrInet4{} siz := unsafe.Sizeof(raw) if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { @@ -121,7 +143,7 @@ func getorigdst(fd uintptr) (Addr, error) { } addr := make([]byte, 1+net.IPv4len+2) - addr[0] = socks5IP4 + addr[0] = socks.ATypeIP4 copy(addr[1:1+net.IPv4len], raw.Addr[:]) port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian addr[1+net.IPv4len], addr[1+net.IPv4len+1] = port[0], port[1] @@ -130,7 +152,7 @@ func getorigdst(fd uintptr) (Addr, error) { // Call ipv6_getorigdst() from linux/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c // NOTE: I haven't tried yet but it should work since Linux 3.8. -func getorigdstIPv6(fd uintptr) (Addr, error) { +func getorigdstIPv6(fd uintptr) (socks.Addr, error) { raw := syscall.RawSockaddrInet6{} siz := unsafe.Sizeof(raw) if err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&raw)), uintptr(unsafe.Pointer(&siz)), 0); err != nil { @@ -138,7 +160,7 @@ func getorigdstIPv6(fd uintptr) (Addr, error) { } addr := make([]byte, 1+net.IPv6len+2) - addr[0] = socks5IP6 + addr[0] = socks.ATypeIP6 copy(addr[1:1+net.IPv6len], raw.Addr[:]) port := (*[2]byte)(unsafe.Pointer(&raw.Port)) // big-endian addr[1+net.IPv6len], addr[1+net.IPv6len+1] = port[0], port[1] diff --git a/redir_linux_386.go b/proxy/redir/redir_linux_386.go similarity index 97% rename from redir_linux_386.go rename to proxy/redir/redir_linux_386.go index d19d48f..c8fca33 100644 --- a/redir_linux_386.go +++ b/proxy/redir/redir_linux_386.go @@ -1,4 +1,4 @@ -package main +package redir import ( "syscall" diff --git a/redir_linux_other.go b/proxy/redir/redir_linux_other.go similarity index 95% rename from redir_linux_other.go rename to proxy/redir/redir_linux_other.go index 6589afc..a2677b9 100644 --- a/redir_linux_other.go +++ b/proxy/redir/redir_linux_other.go @@ -1,6 +1,6 @@ // +build linux,!386 -package main +package redir import "syscall" diff --git a/proxy/server.go b/proxy/server.go new file mode 100644 index 0000000..3e7bd60 --- /dev/null +++ b/proxy/server.go @@ -0,0 +1,48 @@ +package proxy + +import ( + "errors" + "net/url" + "strings" + + "github.com/nadoo/glider/common/log" +) + +// Server interface +type Server interface { + // ListenAndServe as proxy server, use only in server mode. + ListenAndServe() +} + +type ServerCreator func(s string, dialer Dialer) (Server, error) + +var ( + serverMap = make(map[string]ServerCreator) +) + +func RegisterServer(name string, c ServerCreator) { + serverMap[name] = c +} + +func ServerFromURL(s string, dialer Dialer) (Server, error) { + if !strings.Contains(s, "://") { + s = "mixed://" + s + } + + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + if dialer == nil { + dialer = Direct + } + + c, ok := serverMap[strings.ToLower(u.Scheme)] + if ok { + return c(s, dialer) + } + + return nil, errors.New("unknown scheme '" + u.Scheme + "'") +} diff --git a/socks5.go b/proxy/socks5/socks5.go similarity index 62% rename from socks5.go rename to proxy/socks5/socks5.go index b4c1e5a..2d88581 100644 --- a/socks5.go +++ b/proxy/socks5/socks5.go @@ -9,76 +9,72 @@ // socks5 server: // https://github.com/shadowsocks/go-shadowsocks2/tree/master/socks -package main +package socks5 import ( "errors" "io" "net" + "net/url" "strconv" "sync" "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/common/socks" + "github.com/nadoo/glider/proxy" ) -const socks5Version = 5 - -const ( - socks5AuthNone = 0 - socks5AuthPassword = 2 -) - -// SOCKS request commands as defined in RFC 1928 section 4. -const ( - socks5Connect = 1 - socks5Bind = 2 - socks5UDPAssociate = 3 -) - -// SOCKS address types as defined in RFC 1928 section 5. -const ( - socks5IP4 = 1 - socks5Domain = 3 - socks5IP6 = 4 -) - -// MaxAddrLen is the maximum size of SOCKS address in bytes. -const MaxAddrLen = 1 + 1 + 255 + 2 - -// Addr represents a SOCKS address as defined in RFC 1928 section 5. -type Addr []byte - -var socks5Errors = []error{ - errors.New(""), - errors.New("general failure"), - errors.New("connection forbidden"), - errors.New("network unreachable"), - errors.New("host unreachable"), - errors.New("connection refused"), - errors.New("TTL expired"), - errors.New("command not supported"), - errors.New("address type not supported"), - errors.New("socks5UDPAssociate"), -} +const Version = 5 // SOCKS5 struct type SOCKS5 struct { - dialer Dialer + dialer proxy.Dialer addr string user string password string } -// NewSOCKS5 returns a Proxy that makes SOCKSv5 connections to the given address +func init() { + proxy.RegisterDialer("socks5", NewSocks5Dialer) + proxy.RegisterServer("socks5", NewSocks5Server) +} + +// NewSOCKS5 returns a Proxy that makes SOCKS v5 connections to the given address // with an optional username and password. See RFC 1928. -func NewSOCKS5(addr, user, pass string, dialer Dialer) (*SOCKS5, error) { - s := &SOCKS5{ +func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + addr := u.Host + var user, pass string + if u.User != nil { + user = u.User.Username() + pass, _ = u.User.Password() + } + + h := &SOCKS5{ dialer: dialer, addr: addr, user: user, password: pass, } - return s, nil + return h, nil +} + +// NewSocks5Dialer returns a socks5 proxy dialer. +func NewSocks5Dialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewSOCKS5(s, dialer) +} + +// NewSocks5Server returns a socks5 proxy server. +func NewSocks5Server(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewSOCKS5(s, dialer) } // ListenAndServe serves socks5 requests. @@ -91,16 +87,16 @@ func (s *SOCKS5) ListenAndServe() { func (s *SOCKS5) ListenAndServeTCP() { l, err := net.Listen("tcp", s.addr) if err != nil { - logf("proxy-socks5 failed to listen on %s: %v", s.addr, err) + log.F("proxy-socks5 failed to listen on %s: %v", s.addr, err) return } - logf("proxy-socks5 listening TCP on %s", s.addr) + log.F("proxy-socks5 listening TCP on %s", s.addr) for { c, err := l.Accept() if err != nil { - logf("proxy-socks5 failed to accept: %v", err) + log.F("proxy-socks5 failed to accept: %v", err) continue } @@ -119,7 +115,7 @@ func (s *SOCKS5) ServeTCP(c net.Conn) { tgt, err := s.handshake(c) if err != nil { // UDP: keep the connection until disconnect then free the UDP socket - if err == socks5Errors[9] { + if err == socks.Errors[9] { buf := []byte{} // block here for { @@ -127,30 +123,30 @@ func (s *SOCKS5) ServeTCP(c net.Conn) { if err, ok := err.(net.Error); ok && err.Timeout() { continue } - // logf("proxy-socks5 servetcp udp associate end") + // log.F("proxy-socks5 servetcp udp associate end") return } } - logf("proxy-socks5 failed to get target address: %v", err) + log.F("proxy-socks5 failed to get target address: %v", err) return } rc, err := s.dialer.Dial("tcp", tgt.String()) if err != nil { - logf("proxy-socks5 failed to connect to target: %v", err) + log.F("proxy-socks5 failed to connect to target: %v", err) return } defer rc.Close() - logf("proxy-socks5 %s <-> %s", c.RemoteAddr(), tgt) + log.F("proxy-socks5 %s <-> %s", c.RemoteAddr(), tgt) - _, _, err = relay(c, rc) + _, _, err = conn.Relay(c, rc) if err != nil { if err, ok := err.(net.Error); ok && err.Timeout() { return // ignore i/o timeout } - logf("proxy-socks5 relay error: %v", err) + log.F("proxy-socks5 relay error: %v", err) } } @@ -158,22 +154,22 @@ func (s *SOCKS5) ServeTCP(c net.Conn) { func (s *SOCKS5) ListenAndServeUDP() { lc, err := net.ListenPacket("udp", s.addr) if err != nil { - logf("proxy-socks5-udp failed to listen on %s: %v", s.addr, err) + log.F("proxy-socks5-udp failed to listen on %s: %v", s.addr, err) return } defer lc.Close() - logf("proxy-socks5-udp listening UDP on %s", s.addr) + log.F("proxy-socks5-udp listening UDP on %s", s.addr) var nm sync.Map - buf := make([]byte, udpBufSize) + buf := make([]byte, conn.UDPBufSize) for { c := NewSocks5PktConn(lc, nil, nil, true, nil) n, raddr, err := c.ReadFrom(buf) if err != nil { - logf("proxy-socks5-udp remote read error: %v", err) + log.F("proxy-socks5-udp remote read error: %v", err) continue } @@ -181,13 +177,13 @@ func (s *SOCKS5) ListenAndServeUDP() { v, ok := nm.Load(raddr.String()) if !ok && v == nil { if c.tgtAddr == nil { - logf("proxy-socks5-udp can not get target address, not a valid request") + log.F("proxy-socks5-udp can not get target address, not a valid request") continue } lpc, nextHop, err := s.dialer.DialUDP("udp", c.tgtAddr.String()) if err != nil { - logf("proxy-socks5-udp remote dial error: %v", err) + log.F("proxy-socks5-udp remote dial error: %v", err) continue } @@ -195,7 +191,7 @@ func (s *SOCKS5) ListenAndServeUDP() { nm.Store(raddr.String(), pc) go func() { - timedCopy(c, raddr, pc, 2*time.Minute) + conn.TimedCopy(c, raddr, pc, 2*time.Minute) pc.Close() nm.Delete(raddr.String()) }() @@ -206,11 +202,11 @@ func (s *SOCKS5) ListenAndServeUDP() { _, err = pc.WriteTo(buf[:n], pc.writeAddr) if err != nil { - logf("proxy-socks5-udp remote write error: %v", err) + log.F("proxy-socks5-udp remote write error: %v", err) continue } - logf("proxy-socks5-udp %s <-> %s", raddr, c.tgtAddr) + log.F("proxy-socks5-udp %s <-> %s", raddr, c.tgtAddr) } } @@ -219,7 +215,7 @@ func (s *SOCKS5) ListenAndServeUDP() { func (s *SOCKS5) Addr() string { return s.addr } // NextDialer returns the next dialer -func (s *SOCKS5) NextDialer(dstAddr string) Dialer { return s.dialer.NextDialer(dstAddr) } +func (s *SOCKS5) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } // Dial connects to the address addr on the network net via the SOCKS5 proxy. func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) { @@ -231,7 +227,7 @@ func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) { c, err := s.dialer.Dial(network, s.addr) if err != nil { - logf("dial to %s error: %s", s.addr, err) + log.F("dial to %s error: %s", s.addr, err) return nil, err } @@ -251,7 +247,7 @@ func (s *SOCKS5) Dial(network, addr string) (net.Conn, error) { func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) { c, err := s.dialer.Dial("tcp", s.addr) if err != nil { - logf("proxy-socks5 dialudp dial tcp to %s error: %s", s.addr, err) + log.F("proxy-socks5 dialudp dial tcp to %s error: %s", s.addr, err) return nil, nil, err } @@ -262,15 +258,15 @@ func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A // send VER, NMETHODS, METHODS c.Write([]byte{5, 1, 0}) - buf := make([]byte, MaxAddrLen) + buf := make([]byte, socks.MaxAddrLen) // read VER METHOD if _, err := io.ReadFull(c, buf[:2]); err != nil { return nil, nil, err } - dstAddr := ParseAddr(addr) + dstAddr := socks.ParseAddr(addr) // write VER CMD RSV ATYP DST.ADDR DST.PORT - c.Write(append([]byte{5, socks5UDPAssociate, 0}, dstAddr...)) + c.Write(append([]byte{5, socks.CmdUDPAssociate, 0}, dstAddr...)) // read VER REP RSV ATYP BND.ADDR BND.PORT if _, err := io.ReadFull(c, buf[:3]); err != nil { @@ -279,18 +275,18 @@ func (s *SOCKS5) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.A rep := buf[1] if rep != 0 { - logf("proxy-socks5 server reply: %d, not succeeded", rep) + log.F("proxy-socks5 server reply: %d, not succeeded", rep) return nil, nil, errors.New("server connect failed") } - uAddr, err := readAddr(c, buf) + uAddr, err := socks.ReadAddrBuf(c, buf) if err != nil { return nil, nil, err } pc, nextHop, err := s.dialer.DialUDP(network, uAddr.String()) if err != nil { - logf("proxy-socks5 dialudp to %s error: %s", uAddr.String(), err) + log.F("proxy-socks5 dialudp to %s error: %s", uAddr.String(), err) return nil, nil, err } @@ -318,11 +314,11 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error { // the size here is just an estimate buf := make([]byte, 0, 6+len(host)) - buf = append(buf, socks5Version) + buf = append(buf, Version) if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { - buf = append(buf, 2 /* num auth methods */, socks5AuthNone, socks5AuthPassword) + buf = append(buf, 2 /* num auth methods */, socks.AuthNone, socks.AuthPassword) } else { - buf = append(buf, 1 /* num auth methods */, socks5AuthNone) + buf = append(buf, 1 /* num auth methods */, socks.AuthNone) } if _, err := conn.Write(buf); err != nil { @@ -339,7 +335,7 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error { return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") } - if buf[1] == socks5AuthPassword { + if buf[1] == socks.AuthPassword { buf = buf[:0] buf = append(buf, 1 /* password protocol version */) buf = append(buf, uint8(len(s.user))) @@ -361,21 +357,21 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error { } buf = buf[:0] - buf = append(buf, socks5Version, socks5Connect, 0 /* reserved */) + buf = append(buf, Version, socks.CmdConnect, 0 /* reserved */) if ip := net.ParseIP(host); ip != nil { if ip4 := ip.To4(); ip4 != nil { - buf = append(buf, socks5IP4) + buf = append(buf, socks.ATypeIP4) ip = ip4 } else { - buf = append(buf, socks5IP6) + buf = append(buf, socks.ATypeIP6) } buf = append(buf, ip...) } else { if len(host) > 255 { return errors.New("proxy: destination hostname too long: " + host) } - buf = append(buf, socks5Domain) + buf = append(buf, socks.ATypeDomain) buf = append(buf, byte(len(host))) buf = append(buf, host...) } @@ -390,8 +386,8 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error { } failure := "unknown error" - if int(buf[1]) < len(socks5Errors) { - failure = socks5Errors[buf[1]].Error() + if int(buf[1]) < len(socks.Errors) { + failure = socks.Errors[buf[1]].Error() } if len(failure) > 0 { @@ -400,11 +396,11 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error { bytesToDiscard := 0 switch buf[3] { - case socks5IP4: + case socks.ATypeIP4: bytesToDiscard = net.IPv4len - case socks5IP6: + case socks.ATypeIP6: bytesToDiscard = net.IPv6len - case socks5Domain: + case socks.ATypeDomain: _, err := io.ReadFull(conn, buf[:1]) if err != nil { return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) @@ -432,9 +428,9 @@ func (s *SOCKS5) connect(conn net.Conn, target string) error { } // Handshake fast-tracks SOCKS initialization to get target address to connect. -func (s *SOCKS5) handshake(rw io.ReadWriter) (Addr, error) { +func (s *SOCKS5) handshake(rw io.ReadWriter) (socks.Addr, error) { // Read RFC 1928 for request and reply structure and sizes. - buf := make([]byte, MaxAddrLen) + buf := make([]byte, socks.MaxAddrLen) // read VER, NMETHODS, METHODS if _, err := io.ReadFull(rw, buf[:2]); err != nil { return nil, err @@ -452,169 +448,41 @@ func (s *SOCKS5) handshake(rw io.ReadWriter) (Addr, error) { return nil, err } cmd := buf[1] - addr, err := readAddr(rw, buf) + addr, err := socks.ReadAddrBuf(rw, buf) if err != nil { return nil, err } switch cmd { - case socks5Connect: + case socks.CmdConnect: _, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded - case socks5UDPAssociate: - listenAddr := ParseAddr(rw.(net.Conn).LocalAddr().String()) + case socks.CmdUDPAssociate: + listenAddr := socks.ParseAddr(rw.(net.Conn).LocalAddr().String()) _, err = rw.Write(append([]byte{5, 0, 0}, listenAddr...)) // SOCKS v5, reply succeeded if err != nil { - return nil, socks5Errors[7] + return nil, socks.Errors[7] } - err = socks5Errors[9] + err = socks.Errors[9] default: - return nil, socks5Errors[7] + return nil, socks.Errors[7] } return addr, err // skip VER, CMD, RSV fields } -// String serializes SOCKS address a to string form. -func (a Addr) String() string { - var host, port string - - switch ATYP(a[0]) { // address type - case socks5Domain: - host = string(a[2 : 2+int(a[1])]) - port = strconv.Itoa((int(a[2+int(a[1])]) << 8) | int(a[2+int(a[1])+1])) - case socks5IP4: - host = net.IP(a[1 : 1+net.IPv4len]).String() - port = strconv.Itoa((int(a[1+net.IPv4len]) << 8) | int(a[1+net.IPv4len+1])) - case socks5IP6: - host = net.IP(a[1 : 1+net.IPv6len]).String() - port = strconv.Itoa((int(a[1+net.IPv6len]) << 8) | int(a[1+net.IPv6len+1])) - } - - return net.JoinHostPort(host, port) -} - -// UoT udp over tcp -func UoT(b byte) bool { - return b&0x8 == 0x8 -} - -// ATYP return the address type -func ATYP(b byte) int { - return int(b &^ 0x8) -} - -func readAddr(r io.Reader, b []byte) (Addr, error) { - if len(b) < MaxAddrLen { - return nil, io.ErrShortBuffer - } - _, err := io.ReadFull(r, b[:1]) // read 1st byte for address type - if err != nil { - return nil, err - } - - switch ATYP(b[0]) { - case socks5Domain: - _, err = io.ReadFull(r, b[1:2]) // read 2nd byte for domain length - if err != nil { - return nil, err - } - _, err = io.ReadFull(r, b[2:2+int(b[1])+2]) - return b[:1+1+int(b[1])+2], err - case socks5IP4: - _, err = io.ReadFull(r, b[1:1+net.IPv4len+2]) - return b[:1+net.IPv4len+2], err - case socks5IP6: - _, err = io.ReadFull(r, b[1:1+net.IPv6len+2]) - return b[:1+net.IPv6len+2], err - } - - return nil, socks5Errors[8] -} - -// ReadAddr reads just enough bytes from r to get a valid Addr. -func ReadAddr(r io.Reader) (Addr, error) { - return readAddr(r, make([]byte, MaxAddrLen)) -} - -// SplitAddr slices a SOCKS address from beginning of b. Returns nil if failed. -func SplitAddr(b []byte) Addr { - addrLen := 1 - if len(b) < addrLen { - return nil - } - - switch ATYP(b[0]) { - case socks5Domain: - if len(b) < 2 { - return nil - } - addrLen = 1 + 1 + int(b[1]) + 2 - case socks5IP4: - addrLen = 1 + net.IPv4len + 2 - case socks5IP6: - addrLen = 1 + net.IPv6len + 2 - default: - return nil - - } - - if len(b) < addrLen { - return nil - } - - return b[:addrLen] -} - -// ParseAddr parses the address in string s. Returns nil if failed. -func ParseAddr(s string) Addr { - var addr Addr - host, port, err := net.SplitHostPort(s) - if err != nil { - return nil - } - if ip := net.ParseIP(host); ip != nil { - if ip4 := ip.To4(); ip4 != nil { - addr = make([]byte, 1+net.IPv4len+2) - addr[0] = socks5IP4 - copy(addr[1:], ip4) - } else { - addr = make([]byte, 1+net.IPv6len+2) - addr[0] = socks5IP6 - copy(addr[1:], ip) - } - } else { - if len(host) > 255 { - return nil - } - addr = make([]byte, 1+1+len(host)+2) - addr[0] = socks5Domain - addr[1] = byte(len(host)) - copy(addr[2:], host) - } - - portnum, err := strconv.ParseUint(port, 10, 16) - if err != nil { - return nil - } - - addr[len(addr)-2], addr[len(addr)-1] = byte(portnum>>8), byte(portnum) - - return addr -} - // Socks5PktConn . type Socks5PktConn struct { net.PacketConn writeAddr net.Addr // write to and read from addr - tgtAddr Addr + tgtAddr socks.Addr tgtHeader bool ctrlConn net.Conn // tcp control conn } // NewSocks5PktConn returns a Socks5PktConn -func NewSocks5PktConn(c net.PacketConn, writeAddr net.Addr, tgtAddr Addr, tgtHeader bool, ctrlConn net.Conn) *Socks5PktConn { +func NewSocks5PktConn(c net.PacketConn, writeAddr net.Addr, tgtAddr socks.Addr, tgtHeader bool, ctrlConn net.Conn) *Socks5PktConn { pc := &Socks5PktConn{ PacketConn: c, writeAddr: writeAddr, @@ -630,7 +498,7 @@ func NewSocks5PktConn(c net.PacketConn, writeAddr net.Addr, tgtAddr Addr, tgtHea if err, ok := err.(net.Error); ok && err.Timeout() { continue } - logf("proxy-socks5 dialudp udp associate end") + log.F("proxy-socks5 dialudp udp associate end") return } }() @@ -657,7 +525,7 @@ func (pc *Socks5PktConn) ReadFrom(b []byte) (int, net.Addr, error) { // +----+------+------+----------+----------+----------+ // | 2 | 1 | 1 | Variable | 2 | Variable | // +----+------+------+----------+----------+----------+ - tgtAddr := SplitAddr(buf[3:]) + tgtAddr := socks.SplitAddr(buf[3:]) copy(b, buf[3+len(tgtAddr):]) //test diff --git a/ss.go b/proxy/ss/ss.go similarity index 65% rename from ss.go rename to proxy/ss/ss.go index c65f90f..1bcb609 100644 --- a/ss.go +++ b/proxy/ss/ss.go @@ -1,40 +1,73 @@ -package main +package ss import ( "errors" - "log" "net" + "net/url" "strings" "sync" "time" "github.com/shadowsocks/go-shadowsocks2/core" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/common/socks" + "github.com/nadoo/glider/proxy" ) const udpBufSize = 65536 // SS . type SS struct { - dialer Dialer + dialer proxy.Dialer addr string core.Cipher } +func init() { + proxy.RegisterDialer("ss", NewSSDialer) + proxy.RegisterServer("ss", NewSSServer) +} + // NewSS returns a shadowsocks proxy. -func NewSS(addr, method, pass string, dialer Dialer) (*SS, error) { +func NewSS(s string, dialer proxy.Dialer) (*SS, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + addr := u.Host + var method, pass string + if u.User != nil { + method = u.User.Username() + pass, _ = u.User.Password() + } + ciph, err := core.PickCipher(method, nil, pass) if err != nil { log.Fatalf("proxy-ss PickCipher for '%s', error: %s", method, err) } - s := &SS{ + p := &SS{ dialer: dialer, addr: addr, Cipher: ciph, } - return s, nil + return p, nil +} + +// NewSSDialer returns a ss proxy dialer. +func NewSSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewSS(s, dialer) +} + +// NewSSServer returns a ss proxy server. +func NewSSServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewSS(s, dialer) } // ListenAndServe serves ss requests. @@ -47,16 +80,16 @@ func (s *SS) ListenAndServe() { func (s *SS) ListenAndServeTCP() { l, err := net.Listen("tcp", s.addr) if err != nil { - logf("proxy-ss failed to listen on %s: %v", s.addr, err) + log.F("proxy-ss failed to listen on %s: %v", s.addr, err) return } - logf("proxy-ss listening TCP on %s", s.addr) + log.F("proxy-ss listening TCP on %s", s.addr) for { c, err := l.Accept() if err != nil { - logf("proxy-ss failed to accept: %v", err) + log.F("proxy-ss failed to accept: %v", err) continue } go s.ServeTCP(c) @@ -73,28 +106,28 @@ func (s *SS) ServeTCP(c net.Conn) { c = s.StreamConn(c) - tgt, err := ReadAddr(c) + tgt, err := socks.ReadAddr(c) if err != nil { - logf("proxy-ss failed to get target address: %v", err) + log.F("proxy-ss failed to get target address: %v", err) return } dialer := s.dialer.NextDialer(tgt.String()) // udp over tcp? - uot := UoT(tgt[0]) + uot := socks.UoT(tgt[0]) if uot && dialer.Addr() == "DIRECT" { rc, err := net.ListenPacket("udp", "") if err != nil { - logf("proxy-ss UDP remote listen error: %v", err) + log.F("proxy-ss UDP remote listen error: %v", err) } defer rc.Close() req := make([]byte, udpBufSize) n, err := c.Read(req) if err != nil { - logf("proxy-ss error in ioutil.ReadAll: %s\n", err) + log.F("proxy-ss error in ioutil.ReadAll: %s\n", err) return } @@ -104,12 +137,12 @@ func (s *SS) ServeTCP(c net.Conn) { buf := make([]byte, udpBufSize) n, _, err = rc.ReadFrom(buf) if err != nil { - logf("proxy-uottun read error: %v", err) + log.F("proxy-uottun read error: %v", err) } c.Write(buf[:n]) - logf("proxy-ss %s <-tcp-> %s - %s <-udp-> %s ", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt) + log.F("proxy-ss %s <-tcp-> %s - %s <-udp-> %s ", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt) return } @@ -121,19 +154,19 @@ func (s *SS) ServeTCP(c net.Conn) { rc, err := dialer.Dial(network, tgt.String()) if err != nil { - logf("proxy-ss failed to connect to target: %v", err) + log.F("proxy-ss failed to connect to target: %v", err) return } defer rc.Close() - logf("proxy-ss %s <-> %s", c.RemoteAddr(), tgt) + log.F("proxy-ss %s <-> %s", c.RemoteAddr(), tgt) - _, _, err = relay(c, rc) + _, _, err = conn.Relay(c, rc) if err != nil { if err, ok := err.(net.Error); ok && err.Timeout() { return // ignore i/o timeout } - logf("proxy-ss relay error: %v", err) + log.F("proxy-ss relay error: %v", err) } } @@ -142,14 +175,14 @@ func (s *SS) ServeTCP(c net.Conn) { func (s *SS) ListenAndServeUDP() { lc, err := net.ListenPacket("udp", s.addr) if err != nil { - logf("proxy-ss-udp failed to listen on %s: %v", s.addr, err) + log.F("proxy-ss-udp failed to listen on %s: %v", s.addr, err) return } defer lc.Close() lc = s.PacketConn(lc) - logf("proxy-ss-udp listening UDP on %s", s.addr) + log.F("proxy-ss-udp listening UDP on %s", s.addr) var nm sync.Map buf := make([]byte, udpBufSize) @@ -159,7 +192,7 @@ func (s *SS) ListenAndServeUDP() { n, raddr, err := c.ReadFrom(buf) if err != nil { - logf("proxy-ss-udp remote read error: %v", err) + log.F("proxy-ss-udp remote read error: %v", err) continue } @@ -168,7 +201,7 @@ func (s *SS) ListenAndServeUDP() { if !ok && v == nil { lpc, nextHop, err := s.dialer.DialUDP("udp", c.tgtAddr.String()) if err != nil { - logf("proxy-ss-udp remote dial error: %v", err) + log.F("proxy-ss-udp remote dial error: %v", err) continue } @@ -176,7 +209,7 @@ func (s *SS) ListenAndServeUDP() { nm.Store(raddr.String(), pc) go func() { - timedCopy(c, raddr, pc, 2*time.Minute) + conn.TimedCopy(c, raddr, pc, 2*time.Minute) pc.Close() nm.Delete(raddr.String()) }() @@ -187,11 +220,11 @@ func (s *SS) ListenAndServeUDP() { _, err = pc.WriteTo(buf[:n], pc.writeAddr) if err != nil { - logf("proxy-ss-udp remote write error: %v", err) + log.F("proxy-ss-udp remote write error: %v", err) continue } - logf("proxy-ss-udp %s <-> %s", raddr, c.tgtAddr) + log.F("proxy-ss-udp %s <-> %s", raddr, c.tgtAddr) } } @@ -204,11 +237,11 @@ func ListCipher() string { func (s *SS) Addr() string { return s.addr } // NextDialer returns the next dialer -func (s *SS) NextDialer(dstAddr string) Dialer { return s.dialer.NextDialer(dstAddr) } +func (s *SS) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } // Dial connects to the address addr on the network net via the proxy. func (s *SS) Dial(network, addr string) (net.Conn, error) { - target := ParseAddr(addr) + target := socks.ParseAddr(addr) if target == nil { return nil, errors.New("proxy-ss unable to parse address: " + addr) } @@ -219,7 +252,7 @@ func (s *SS) Dial(network, addr string) (net.Conn, error) { c, err := s.dialer.Dial("tcp", s.addr) if err != nil { - logf("proxy-ss dial to %s error: %s", s.addr, err) + log.F("proxy-ss dial to %s error: %s", s.addr, err) return nil, err } @@ -241,11 +274,11 @@ func (s *SS) Dial(network, addr string) (net.Conn, error) { func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) { pc, nextHop, err := s.dialer.DialUDP(network, s.addr) if err != nil { - logf("proxy-ss dialudp to %s error: %s", s.addr, err) + log.F("proxy-ss dialudp to %s error: %s", s.addr, err) return nil, nil, err } - pkc := NewPktConn(s.PacketConn(pc), nextHop, ParseAddr(addr), true) + pkc := NewPktConn(s.PacketConn(pc), nextHop, socks.ParseAddr(addr), true) return pkc, nextHop, err } @@ -255,12 +288,12 @@ type PktConn struct { writeAddr net.Addr // write to and read from addr - tgtAddr Addr + tgtAddr socks.Addr tgtHeader bool } // NewPktConn returns a PktConn -func NewPktConn(c net.PacketConn, writeAddr net.Addr, tgtAddr Addr, tgtHeader bool) *PktConn { +func NewPktConn(c net.PacketConn, writeAddr net.Addr, tgtAddr socks.Addr, tgtHeader bool) *PktConn { pc := &PktConn{ PacketConn: c, writeAddr: writeAddr, @@ -281,7 +314,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) { return n, raddr, err } - tgtAddr := SplitAddr(buf) + tgtAddr := socks.SplitAddr(buf) copy(b, buf[len(tgtAddr):]) //test diff --git a/ssr.go b/proxy/ssr/ssr.go similarity index 72% rename from ssr.go rename to proxy/ssr/ssr.go index 8fc369e..6f9b7b7 100644 --- a/ssr.go +++ b/proxy/ssr/ssr.go @@ -1,4 +1,4 @@ -package main +package ssr import ( "errors" @@ -11,11 +11,15 @@ import ( "github.com/sun8911879/shadowsocksR/obfs" "github.com/sun8911879/shadowsocksR/protocol" "github.com/sun8911879/shadowsocksR/ssr" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/common/socks" + "github.com/nadoo/glider/proxy" ) // SSR . type SSR struct { - dialer Dialer + dialer proxy.Dialer addr string EncryptMethod string @@ -28,41 +32,63 @@ type SSR struct { ProtocolData interface{} } +func init() { + proxy.RegisterDialer("ssr", NewSSRDialer) +} + // NewSSR returns a shadowsocksr proxy, ssr://method:pass@host:port/rawQuery -func NewSSR(addr, method, pass, rawQuery string, dialer Dialer) (*SSR, error) { - s := &SSR{ +func NewSSR(s string, dialer proxy.Dialer) (*SSR, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + addr := u.Host + var method, pass string + if u.User != nil { + method = u.User.Username() + pass, _ = u.User.Password() + } + + p := &SSR{ dialer: dialer, addr: addr, EncryptMethod: method, EncryptPassword: pass, } - p, _ := url.ParseQuery(rawQuery) - if v, ok := p["protocol"]; ok { - s.Protocol = v[0] + q, _ := url.ParseQuery(u.RawQuery) + if v, ok := q["protocol"]; ok { + p.Protocol = v[0] } - if v, ok := p["protocol_param"]; ok { - s.ProtocolParam = v[0] + if v, ok := q["protocol_param"]; ok { + p.ProtocolParam = v[0] } - if v, ok := p["obfs"]; ok { - s.Obfs = v[0] + if v, ok := q["obfs"]; ok { + p.Obfs = v[0] } - if v, ok := p["obfs_param"]; ok { - s.ObfsParam = v[0] + if v, ok := q["obfs_param"]; ok { + p.ObfsParam = v[0] } - return s, nil + return p, nil +} + +// NewSSRDialer returns a ssr proxy dialer. +func NewSSRDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) { + return NewSSR(s, dialer) } // Addr returns forwarder's address func (s *SSR) Addr() string { return s.addr } // NextDialer returns the next dialer -func (s *SSR) NextDialer(dstAddr string) Dialer { return s.dialer.NextDialer(dstAddr) } +func (s *SSR) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) } // Dial connects to the address addr on the network net via the proxy. func (s *SSR) Dial(network, addr string) (net.Conn, error) { - target := ParseAddr(addr) + target := socks.ParseAddr(addr) if target == nil { return nil, errors.New("proxy-ssr unable to parse address: " + addr) } @@ -74,7 +100,7 @@ func (s *SSR) Dial(network, addr string) (net.Conn, error) { c, err := s.dialer.Dial("tcp", s.addr) if err != nil { - logf("proxy-ssr dial to %s error: %s", s.addr, err) + log.F("proxy-ssr dial to %s error: %s", s.addr, err) return nil, err } diff --git a/proxy/tcptun/tcptun.go b/proxy/tcptun/tcptun.go new file mode 100644 index 0000000..f212219 --- /dev/null +++ b/proxy/tcptun/tcptun.go @@ -0,0 +1,94 @@ +package tcptun + +import ( + "net" + "net/url" + "strings" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +// TCPTun struct +type TCPTun struct { + dialer proxy.Dialer + addr string + + raddr string +} + +func init() { + proxy.RegisterServer("tcptun", NewTCPTunServer) +} + +// NewTCPTun returns a tcptun proxy. +func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + addr := u.Host + d := strings.Split(addr, "=") + + p := &TCPTun{ + dialer: dialer, + addr: d[0], + raddr: d[1], + } + + return p, nil +} + +// NewTCPTunServer returns a udp tunnel server. +func NewTCPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewTCPTun(s, dialer) +} + +// ListenAndServe . +func (s *TCPTun) ListenAndServe() { + l, err := net.Listen("tcp", s.addr) + if err != nil { + log.F("failed to listen on %s: %v", s.addr, err) + return + } + + log.F("listening TCP on %s", s.addr) + + for { + c, err := l.Accept() + if err != nil { + log.F("failed to accept: %v", err) + continue + } + + go func() { + defer c.Close() + + if c, ok := c.(*net.TCPConn); ok { + c.SetKeepAlive(true) + } + + rc, err := s.dialer.Dial("tcp", s.raddr) + if err != nil { + + log.F("failed to connect to target: %v", err) + return + } + defer rc.Close() + + log.F("proxy-tcptun %s <-> %s", c.RemoteAddr(), s.raddr) + + _, _, err = conn.Relay(c, rc) + if err != nil { + if err, ok := err.(net.Error); ok && err.Timeout() { + return // ignore i/o timeout + } + log.F("relay error: %v", err) + } + + }() + } +} diff --git a/tproxy.go b/proxy/tproxy/tproxy.go similarity index 100% rename from tproxy.go rename to proxy/tproxy/tproxy.go diff --git a/tproxy_other.go b/proxy/tproxy/tproxy_other.go similarity index 100% rename from tproxy_other.go rename to proxy/tproxy/tproxy_other.go diff --git a/proxy/udptun/udptun.go b/proxy/udptun/udptun.go new file mode 100644 index 0000000..36c15de --- /dev/null +++ b/proxy/udptun/udptun.go @@ -0,0 +1,106 @@ +package udptun + +import ( + "net" + "net/url" + "strings" + "sync" + "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +// UDPTun struct +type UDPTun struct { + dialer proxy.Dialer + addr string + + raddr string +} + +func init() { + proxy.RegisterServer("udptun", NewUDPTunServer) +} + +// NewUDPTun returns a UDPTun proxy. +func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + addr := u.Host + d := strings.Split(addr, "=") + + p := &UDPTun{ + dialer: dialer, + addr: d[0], + raddr: d[1], + } + + return p, nil +} + +// NewUDPTunServer returns a udp tunnel server. +func NewUDPTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewUDPTun(s, dialer) +} + +// ListenAndServe . +func (s *UDPTun) ListenAndServe() { + c, err := net.ListenPacket("udp", s.addr) + if err != nil { + log.F("proxy-udptun failed to listen on %s: %v", s.addr, err) + return + } + defer c.Close() + + log.F("proxy-udptun listening UDP on %s", s.addr) + + var nm sync.Map + buf := make([]byte, conn.UDPBufSize) + + for { + n, raddr, err := c.ReadFrom(buf) + if err != nil { + log.F("proxy-udptun read error: %v", err) + continue + } + + var pc net.PacketConn + var writeAddr net.Addr + + v, ok := nm.Load(raddr.String()) + if !ok && v == nil { + + pc, writeAddr, err = s.dialer.DialUDP("udp", s.raddr) + if err != nil { + log.F("proxy-udptun remote dial error: %v", err) + continue + } + + nm.Store(raddr.String(), pc) + + go func() { + conn.TimedCopy(c, raddr, pc, 2*time.Minute) + pc.Close() + nm.Delete(raddr.String()) + }() + + } else { + pc = v.(net.PacketConn) + } + + _, err = pc.WriteTo(buf[:n], writeAddr) + if err != nil { + log.F("proxy-udptun remote write error: %v", err) + continue + } + + log.F("proxy-udptun %s <-> %s", raddr, s.raddr) + + } +} diff --git a/proxy/uottun/uottun.go b/proxy/uottun/uottun.go new file mode 100644 index 0000000..b082a86 --- /dev/null +++ b/proxy/uottun/uottun.go @@ -0,0 +1,104 @@ +package uottun + +import ( + "io/ioutil" + "net" + "net/url" + "strings" + "time" + + "github.com/nadoo/glider/common/conn" + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" +) + +// UoTTun udp over tcp tunnel +type UoTTun struct { + dialer proxy.Dialer + addr string + + raddr string +} + +func init() { + proxy.RegisterServer("uottun", NewUoTTunServer) +} + +// NewUoTTun returns a UoTTun proxy. +func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) { + u, err := url.Parse(s) + if err != nil { + log.F("parse err: %s", err) + return nil, err + } + + addr := u.Host + d := strings.Split(addr, "=") + + p := &UoTTun{ + dialer: dialer, + addr: d[0], + raddr: d[1], + } + + return p, nil +} + +// NewUoTTunServer returns a uot tunnel server. +func NewUoTTunServer(s string, dialer proxy.Dialer) (proxy.Server, error) { + return NewUoTTun(s, dialer) +} + +// ListenAndServe . +func (s *UoTTun) ListenAndServe() { + c, err := net.ListenPacket("udp", s.addr) + if err != nil { + log.F("proxy-uottun failed to listen on %s: %v", s.addr, err) + return + } + defer c.Close() + + log.F("proxy-uottun listening UDP on %s", s.addr) + + buf := make([]byte, conn.UDPBufSize) + + for { + n, clientAddr, err := c.ReadFrom(buf) + if err != nil { + log.F("proxy-uottun read error: %v", err) + continue + } + + rc, err := s.dialer.Dial("uot", s.raddr) + if err != nil { + log.F("proxy-uottun failed to connect to server %v: %v", s.raddr, err) + continue + } + + go func() { + // no remote forwarder, just a local udp forwarder + if urc, ok := rc.(*net.UDPConn); ok { + conn.TimedCopy(c, clientAddr, urc, 2*time.Minute) + urc.Close() + return + } + + // remote forwarder, udp over tcp + resp, err := ioutil.ReadAll(rc) + if err != nil { + log.F("error in ioutil.ReadAll: %s\n", err) + return + } + rc.Close() + c.WriteTo(resp, clientAddr) + }() + + _, err = rc.Write(buf[:n]) + if err != nil { + log.F("proxy-uottun remote write error: %v", err) + continue + } + + log.F("proxy-uottun %s <-> %s", clientAddr, s.raddr) + } +} diff --git a/redir_other.go b/redir_other.go deleted file mode 100644 index c26f857..0000000 --- a/redir_other.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build !linux - -package main - -import ( - "errors" - "log" -) - -// RedirProxy struct -type RedirProxy struct{} - -// NewRedirProxy returns a redirect proxy. -func NewRedirProxy(addr string, sDialer Dialer) (*RedirProxy, error) { - return nil, errors.New("redir not supported on this os") -} - -// ListenAndServe . -func (s *RedirProxy) ListenAndServe() { - log.Fatal("redir not supported on this os") -} diff --git a/rule.go b/rule.go index dca32c0..c837140 100644 --- a/rule.go +++ b/rule.go @@ -1,15 +1,17 @@ package main import ( - "log" "net" "strings" "sync" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" ) // RuleDialer struct type RuleDialer struct { - gDialer Dialer + gDialer proxy.Dialer domainMap sync.Map ipMap sync.Map @@ -17,16 +19,16 @@ type RuleDialer struct { } // NewRuleDialer returns a new rule dialer -func NewRuleDialer(rules []*RuleConf, gDialer Dialer) *RuleDialer { +func NewRuleDialer(rules []*RuleConf, gDialer proxy.Dialer) *RuleDialer { rd := &RuleDialer{gDialer: gDialer} for _, r := range rules { - var fwdrs []Dialer + var fwdrs []proxy.Dialer for _, chain := range r.Forward { - var fwdr Dialer + var fwdr proxy.Dialer var err error for _, url := range strings.Split(chain, ",") { - fwdr, err = DialerFromURL(url, fwdr) + fwdr, err = proxy.DialerFromURL(url, fwdr) if err != nil { log.Fatal(err) } @@ -59,7 +61,7 @@ func NewRuleDialer(rules []*RuleConf, gDialer Dialer) *RuleDialer { func (rd *RuleDialer) Addr() string { return "RULE DIALER, DEFAULT: " + rd.gDialer.Addr() } // NextDialer return next dialer according to rule -func (rd *RuleDialer) NextDialer(dstAddr string) Dialer { +func (rd *RuleDialer) NextDialer(dstAddr string) proxy.Dialer { host, _, err := net.SplitHostPort(dstAddr) if err != nil { // TODO: check here @@ -71,15 +73,15 @@ func (rd *RuleDialer) NextDialer(dstAddr string) Dialer { if ip := net.ParseIP(host); ip != nil { // check ip if dialer, ok := rd.ipMap.Load(ip.String()); ok { - return dialer.(Dialer) + return dialer.(proxy.Dialer) } - var ret Dialer + var ret proxy.Dialer // check cidr rd.cidrMap.Range(func(key, value interface{}) bool { cidr := key.(*net.IPNet) if cidr.Contains(ip) { - ret = value.(Dialer) + ret = value.(proxy.Dialer) return false } @@ -99,7 +101,7 @@ func (rd *RuleDialer) NextDialer(dstAddr string) Dialer { // find in domainMap if dialer, ok := rd.domainMap.Load(domain); ok { - return dialer.(Dialer) + return dialer.(proxy.Dialer) } } @@ -127,7 +129,7 @@ func (rd *RuleDialer) AddDomainIP(domain, ip string) error { // find in domainMap if dialer, ok := rd.domainMap.Load(pDomain); ok { rd.ipMap.Store(ip, dialer) - logf("rule add ip=%s, based on rule: domain=%s & domain/ip: %s/%s\n", ip, pDomain, domain, ip) + log.F("rule add ip=%s, based on rule: domain=%s & domain/ip: %s/%s\n", ip, pDomain, domain, ip) } } diff --git a/server.go b/server.go deleted file mode 100644 index 559099b..0000000 --- a/server.go +++ /dev/null @@ -1,65 +0,0 @@ -package main - -import ( - "errors" - "net/url" - "strings" -) - -// Server interface -type Server interface { - // ListenAndServe as proxy server, use only in server mode. - ListenAndServe() -} - -// ServerFromURL parses url and get a Proxy -// TODO: table -func ServerFromURL(s string, dialer Dialer) (Server, error) { - if !strings.Contains(s, "://") { - s = "mixed://" + s - } - - u, err := url.Parse(s) - if err != nil { - logf("parse err: %s", err) - return nil, err - } - - addr := u.Host - var user, pass string - if u.User != nil { - user = u.User.Username() - pass, _ = u.User.Password() - } - - if dialer == nil { - dialer = Direct - } - - switch u.Scheme { - case "mixed": - return NewMixedProxy(addr, user, pass, u.RawQuery, dialer) - case "http": - return NewHTTP(addr, user, pass, u.RawQuery, dialer) - case "socks5": - return NewSOCKS5(addr, user, pass, dialer) - case "ss": - return NewSS(addr, user, pass, dialer) - case "redir": - return NewRedirProxy(addr, dialer) - case "tcptun": - d := strings.Split(addr, "=") - return NewTCPTun(d[0], d[1], dialer) - case "udptun": - d := strings.Split(addr, "=") - return NewUDPTun(d[0], d[1], dialer) - case "dnstun": - d := strings.Split(addr, "=") - return NewDNSTun(d[0], d[1], dialer) - case "uottun": - d := strings.Split(addr, "=") - return NewUoTTun(d[0], d[1], dialer) - } - - return nil, errors.New("unknown scheme '" + u.Scheme + "'") -} diff --git a/strategy.go b/strategy.go index 668bee0..a04d1f5 100644 --- a/strategy.go +++ b/strategy.go @@ -7,28 +7,31 @@ import ( "strings" "sync" "time" + + "github.com/nadoo/glider/common/log" + "github.com/nadoo/glider/proxy" ) -// NewStrategyDialer returns a new Strategy Dialer -func NewStrategyDialer(strategy string, dialers []Dialer, website string, interval int) Dialer { +// NewStrategyDialer returns a new Strategy proxy.Dialer +func NewStrategyDialer(strategy string, dialers []proxy.Dialer, website string, interval int) proxy.Dialer { if len(dialers) == 0 { - return Direct + return proxy.Direct } if len(dialers) == 1 { return dialers[0] } - var dialer Dialer + var dialer proxy.Dialer switch strategy { case "rr": dialer = newRRDialer(dialers, website, interval) - logf("forward to remote servers in round robin mode.") + log.F("forward to remote servers in round robin mode.") case "ha": dialer = newHADialer(dialers, website, interval) - logf("forward to remote servers in high availability mode.") + log.F("forward to remote servers in high availability mode.") default: - logf("not supported forward mode '%s', just use the first forward server.", conf.Strategy) + log.F("not supported forward mode '%s', just use the first forward server.", conf.Strategy) dialer = dialers[0] } @@ -37,7 +40,7 @@ func NewStrategyDialer(strategy string, dialers []Dialer, website string, interv // rrDialer is the base struct of strategy dialer type rrDialer struct { - dialers []Dialer + dialers []proxy.Dialer idx int status sync.Map @@ -48,7 +51,7 @@ type rrDialer struct { } // newRRDialer returns a new rrDialer -func newRRDialer(dialers []Dialer, website string, interval int) *rrDialer { +func newRRDialer(dialers []proxy.Dialer, website string, interval int) *rrDialer { rr := &rrDialer{dialers: dialers} rr.website = website @@ -71,7 +74,7 @@ func (rr *rrDialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo ne return rr.NextDialer(addr).DialUDP(network, addr) } -func (rr *rrDialer) NextDialer(dstAddr string) Dialer { +func (rr *rrDialer) NextDialer(dstAddr string) proxy.Dialer { n := len(rr.dialers) if n == 1 { rr.idx = 0 @@ -88,7 +91,7 @@ func (rr *rrDialer) NextDialer(dstAddr string) Dialer { } if !found { - logf("NO AVAILABLE PROXY FOUND! please check your network or proxy server settings.") + log.F("NO AVAILABLE PROXY FOUND! please check your network or proxy server settings.") } return rr.dialers[rr.idx] @@ -117,7 +120,7 @@ func (rr *rrDialer) checkDialer(idx int) { c, err := d.Dial("tcp", rr.website) if err != nil { rr.status.Store(idx, false) - logf("proxy-check %s -> %s, set to DISABLED. error in dial: %s", d.Addr(), rr.website, err) + log.F("proxy-check %s -> %s, set to DISABLED. error in dial: %s", d.Addr(), rr.website, err) continue } @@ -126,15 +129,15 @@ func (rr *rrDialer) checkDialer(idx int) { _, err = io.ReadFull(c, buf) if err != nil { rr.status.Store(idx, false) - logf("proxy-check %s -> %s, set to DISABLED. error in read: %s", d.Addr(), rr.website, err) + log.F("proxy-check %s -> %s, set to DISABLED. error in read: %s", d.Addr(), rr.website, err) } else if bytes.Equal([]byte("HTTP"), buf) { rr.status.Store(idx, true) retry = 2 dialTime := time.Since(startTime) - logf("proxy-check %s -> %s, set to ENABLED. connect time: %s", d.Addr(), rr.website, dialTime.String()) + log.F("proxy-check %s -> %s, set to ENABLED. connect time: %s", d.Addr(), rr.website, dialTime.String()) } else { rr.status.Store(idx, false) - logf("proxy-check %s -> %s, set to DISABLED. server response: %s", d.Addr(), rr.website, buf) + log.F("proxy-check %s -> %s, set to DISABLED. server response: %s", d.Addr(), rr.website, buf) } c.Close() @@ -147,7 +150,7 @@ type haDialer struct { } // newHADialer . -func newHADialer(dialers []Dialer, webhost string, duration int) Dialer { +func newHADialer(dialers []proxy.Dialer, webhost string, duration int) proxy.Dialer { return &haDialer{rrDialer: newRRDialer(dialers, webhost, duration)} } diff --git a/tcptun.go b/tcptun.go deleted file mode 100644 index f671bd1..0000000 --- a/tcptun.go +++ /dev/null @@ -1,68 +0,0 @@ -package main - -import "net" - -// TCPTun struct -type TCPTun struct { - dialer Dialer - addr string - - raddr string -} - -// NewTCPTun returns a tcptun proxy. -func NewTCPTun(addr, raddr string, dialer Dialer) (*TCPTun, error) { - s := &TCPTun{ - dialer: dialer, - addr: addr, - raddr: raddr, - } - - return s, nil -} - -// ListenAndServe . -func (s *TCPTun) ListenAndServe() { - l, err := net.Listen("tcp", s.addr) - if err != nil { - logf("failed to listen on %s: %v", s.addr, err) - return - } - - logf("listening TCP on %s", s.addr) - - for { - c, err := l.Accept() - if err != nil { - logf("failed to accept: %v", err) - continue - } - - go func() { - defer c.Close() - - if c, ok := c.(*net.TCPConn); ok { - c.SetKeepAlive(true) - } - - rc, err := s.dialer.Dial("tcp", s.raddr) - if err != nil { - - logf("failed to connect to target: %v", err) - return - } - defer rc.Close() - - logf("proxy-tcptun %s <-> %s", c.RemoteAddr(), s.raddr) - - _, _, err = relay(c, rc) - if err != nil { - if err, ok := err.(net.Error); ok && err.Timeout() { - return // ignore i/o timeout - } - logf("relay error: %v", err) - } - - }() - } -} diff --git a/udptun.go b/udptun.go deleted file mode 100644 index 498d103..0000000 --- a/udptun.go +++ /dev/null @@ -1,82 +0,0 @@ -package main - -import ( - "net" - "sync" - "time" -) - -// UDPTun struct -type UDPTun struct { - dialer Dialer - addr string - - raddr string -} - -// NewUDPTun returns a UDPTun proxy. -func NewUDPTun(addr, raddr string, dialer Dialer) (*UDPTun, error) { - s := &UDPTun{ - dialer: dialer, - addr: addr, - raddr: raddr, - } - - return s, nil -} - -// ListenAndServe . -func (s *UDPTun) ListenAndServe() { - c, err := net.ListenPacket("udp", s.addr) - if err != nil { - logf("proxy-udptun failed to listen on %s: %v", s.addr, err) - return - } - defer c.Close() - - logf("proxy-udptun listening UDP on %s", s.addr) - - var nm sync.Map - buf := make([]byte, udpBufSize) - - for { - n, raddr, err := c.ReadFrom(buf) - if err != nil { - logf("proxy-udptun read error: %v", err) - continue - } - - var pc net.PacketConn - var writeAddr net.Addr - - v, ok := nm.Load(raddr.String()) - if !ok && v == nil { - - pc, writeAddr, err = s.dialer.DialUDP("udp", s.raddr) - if err != nil { - logf("proxy-udptun remote dial error: %v", err) - continue - } - - nm.Store(raddr.String(), pc) - - go func() { - timedCopy(c, raddr, pc, 2*time.Minute) - pc.Close() - nm.Delete(raddr.String()) - }() - - } else { - pc = v.(net.PacketConn) - } - - _, err = pc.WriteTo(buf[:n], writeAddr) - if err != nil { - logf("proxy-udptun remote write error: %v", err) - continue - } - - logf("proxy-udptun %s <-> %s", raddr, s.raddr) - - } -} diff --git a/uottun.go b/uottun.go deleted file mode 100644 index 1f633e1..0000000 --- a/uottun.go +++ /dev/null @@ -1,80 +0,0 @@ -package main - -import ( - "io/ioutil" - "net" - "time" -) - -// UoTTun udp over tcp tunnel -type UoTTun struct { - dialer Dialer - addr string - - raddr string -} - -// NewUoTTun returns a UoTTun proxy. -func NewUoTTun(addr, raddr string, dialer Dialer) (*UoTTun, error) { - s := &UoTTun{ - dialer: dialer, - addr: addr, - raddr: raddr, - } - - return s, nil -} - -// ListenAndServe . -func (s *UoTTun) ListenAndServe() { - c, err := net.ListenPacket("udp", s.addr) - if err != nil { - logf("proxy-uottun failed to listen on %s: %v", s.addr, err) - return - } - defer c.Close() - - logf("proxy-uottun listening UDP on %s", s.addr) - - buf := make([]byte, udpBufSize) - - for { - n, clientAddr, err := c.ReadFrom(buf) - if err != nil { - logf("proxy-uottun read error: %v", err) - continue - } - - rc, err := s.dialer.Dial("uot", s.raddr) - if err != nil { - logf("proxy-uottun failed to connect to server %v: %v", s.raddr, err) - continue - } - - go func() { - // no remote forwarder, just a local udp forwarder - if urc, ok := rc.(*net.UDPConn); ok { - timedCopy(c, clientAddr, urc, 2*time.Minute) - urc.Close() - return - } - - // remote forwarder, udp over tcp - resp, err := ioutil.ReadAll(rc) - if err != nil { - logf("error in ioutil.ReadAll: %s\n", err) - return - } - rc.Close() - c.WriteTo(resp, clientAddr) - }() - - _, err = rc.Write(buf[:n]) - if err != nil { - logf("proxy-uottun remote write error: %v", err) - continue - } - - logf("proxy-uottun %s <-> %s", clientAddr, s.raddr) - } -}