mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
forwarder: add the ability to get parameters like 'priority'
This commit is contained in:
parent
e3888a6bd3
commit
a46ab20901
14
conf.go
14
conf.go
@ -27,11 +27,11 @@ var conf struct {
|
||||
RulesDir string
|
||||
|
||||
DNS string
|
||||
DNSServer []string
|
||||
DNSServers []string
|
||||
DNSTimeout int
|
||||
DNSMaxTTL int
|
||||
DNSMinTTL int
|
||||
DNSRecord []string
|
||||
DNSRecords []string
|
||||
|
||||
IPSet string
|
||||
|
||||
@ -51,11 +51,11 @@ func confInit() {
|
||||
flag.StringVar(&conf.RulesDir, "rules-dir", "", "rule file folder")
|
||||
|
||||
flag.StringVar(&conf.DNS, "dns", "", "dns forwarder server listen address")
|
||||
flag.StringSliceUniqVar(&conf.DNSServer, "dnsserver", []string{"8.8.8.8:53"}, "remote dns server")
|
||||
flag.StringSliceUniqVar(&conf.DNSServers, "dnsserver", []string{"8.8.8.8:53"}, "remote dns server")
|
||||
flag.IntVar(&conf.DNSTimeout, "dnstimeout", 3, "timeout value used in multiple dnsservers switch(seconds)")
|
||||
flag.IntVar(&conf.DNSMaxTTL, "dnsmaxttl", 1800, "maximum TTL value for entries in the CACHE(seconds)")
|
||||
flag.IntVar(&conf.DNSMinTTL, "dnsminttl", 0, "minimum TTL value for entries in the CACHE(seconds)")
|
||||
flag.StringSliceUniqVar(&conf.DNSRecord, "dnsrecord", nil, "custom dns record, format: domain/ip")
|
||||
flag.StringSliceUniqVar(&conf.DNSRecords, "dnsrecord", nil, "custom dns record, format: domain/ip")
|
||||
|
||||
flag.StringVar(&conf.IPSet, "ipset", "", "ipset name")
|
||||
|
||||
@ -127,8 +127,8 @@ type RuleConf struct {
|
||||
Forward []string
|
||||
StrategyConfig strategy.Config
|
||||
|
||||
DNSServer []string
|
||||
IPSet string
|
||||
DNSServers []string
|
||||
IPSet string
|
||||
|
||||
Domain []string
|
||||
IP []string
|
||||
@ -145,7 +145,7 @@ func NewRuleConfFromFile(ruleFile string) (*RuleConf, error) {
|
||||
f.StringVar(&p.StrategyConfig.CheckWebSite, "checkwebsite", "www.apple.com", "proxy check HTTP(NOT HTTPS) website address, format: HOST[:PORT], default port: 80")
|
||||
f.IntVar(&p.StrategyConfig.CheckInterval, "checkduration", 30, "proxy check interval(seconds)")
|
||||
|
||||
f.StringSliceUniqVar(&p.DNSServer, "dnsserver", nil, "remote dns server")
|
||||
f.StringSliceUniqVar(&p.DNSServers, "dnsserver", nil, "remote dns server")
|
||||
f.StringVar(&p.IPSet, "ipset", "", "ipset name")
|
||||
|
||||
f.StringSliceUniqVar(&p.Domain, "domain", nil, "domain")
|
||||
|
@ -18,9 +18,11 @@ type HandleFunc func(Domain, ip string) error
|
||||
|
||||
// Config for dns
|
||||
type Config struct {
|
||||
Servers []string
|
||||
Timeout int
|
||||
MaxTTL int
|
||||
MinTTL int
|
||||
Records []string
|
||||
}
|
||||
|
||||
// Client is a dns client struct
|
||||
@ -34,15 +36,20 @@ type Client struct {
|
||||
}
|
||||
|
||||
// NewClient returns a new dns client
|
||||
func NewClient(dialer proxy.Dialer, upServers []string, config *Config) (*Client, error) {
|
||||
func NewClient(dialer proxy.Dialer, config *Config) (*Client, error) {
|
||||
c := &Client{
|
||||
dialer: dialer,
|
||||
cache: NewCache(),
|
||||
config: config,
|
||||
upServers: upServers,
|
||||
upServers: config.Servers,
|
||||
upServerMap: make(map[string][]string),
|
||||
}
|
||||
|
||||
// custom records
|
||||
for _, record := range config.Records {
|
||||
c.AddRecord(record)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,8 @@ type Server struct {
|
||||
}
|
||||
|
||||
// NewServer returns a new dns server
|
||||
func NewServer(addr string, dialer proxy.Dialer, upServers []string, config *Config) (*Server, error) {
|
||||
c, err := NewClient(dialer, upServers, config)
|
||||
func NewServer(addr string, dialer proxy.Dialer, config *Config) (*Server, error) {
|
||||
c, err := NewClient(dialer, config)
|
||||
s := &Server{
|
||||
addr: addr,
|
||||
Client: c,
|
||||
|
15
main.go
15
main.go
@ -41,25 +41,22 @@ func main() {
|
||||
// DNS Server
|
||||
if conf.DNS != "" {
|
||||
dnscfg := &dns.Config{
|
||||
Servers: conf.DNSServers,
|
||||
Timeout: conf.DNSTimeout,
|
||||
MaxTTL: conf.DNSMaxTTL,
|
||||
MinTTL: conf.DNSMinTTL}
|
||||
MinTTL: conf.DNSMinTTL,
|
||||
Records: conf.DNSRecords}
|
||||
|
||||
d, err := dns.NewServer(conf.DNS, dialer, conf.DNSServer, dnscfg)
|
||||
d, err := dns.NewServer(conf.DNS, dialer, dnscfg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// custom records
|
||||
for _, record := range conf.DNSRecord {
|
||||
d.AddRecord(record)
|
||||
}
|
||||
|
||||
// rule
|
||||
for _, r := range conf.rules {
|
||||
for _, domain := range r.Domain {
|
||||
if len(r.DNSServer) > 0 {
|
||||
d.SetServer(domain, r.DNSServer...)
|
||||
if len(r.DNSServers) > 0 {
|
||||
d.SetServer(domain, r.DNSServers...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,40 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Forwarder is a forwarder
|
||||
type Forwarder struct {
|
||||
Dialer
|
||||
Priority int
|
||||
addr string
|
||||
disabled bool
|
||||
failures int
|
||||
priority int
|
||||
weight int
|
||||
disabled uint32
|
||||
failures uint32
|
||||
latency int
|
||||
}
|
||||
|
||||
// ForwarderFromURL returns a new forwarder
|
||||
func ForwarderFromURL(s string) (*Forwarder, error) {
|
||||
func ForwarderFromURL(s string) (f *Forwarder, err error) {
|
||||
ss := strings.Split(s, "#")
|
||||
var d Dialer
|
||||
var err error
|
||||
for _, url := range strings.Split(s, ",") {
|
||||
for _, url := range strings.Split(ss[0], ",") {
|
||||
d, err = DialerFromURL(url, d)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &Forwarder{Dialer: d}, nil
|
||||
f = NewForwarder(d)
|
||||
if len(ss) > 1 {
|
||||
err = f.parseOption(ss[1])
|
||||
}
|
||||
|
||||
return f, err
|
||||
}
|
||||
|
||||
// NewForwarder .
|
||||
@ -34,17 +42,61 @@ func NewForwarder(dialer Dialer) *Forwarder {
|
||||
return &Forwarder{Dialer: dialer, addr: dialer.Addr()}
|
||||
}
|
||||
|
||||
func (f *Forwarder) parseOption(option string) error {
|
||||
query, err := url.ParseQuery(option)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var priority uint64
|
||||
p := query.Get("priority")
|
||||
if p != "" {
|
||||
priority, err = strconv.ParseUint(p, 10, 32)
|
||||
}
|
||||
f.Priority = int(priority)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Addr .
|
||||
func (f *Forwarder) Addr() string {
|
||||
return f.addr
|
||||
}
|
||||
|
||||
// Dial .
|
||||
func (f *Forwarder) Dial(network, addr string) (c net.Conn, err error) {
|
||||
c, err = f.Dialer.Dial(network, addr)
|
||||
|
||||
// TODO: proxy timeout, target timeout?
|
||||
if err != nil {
|
||||
atomic.AddUint32(&f.failures, 1)
|
||||
// log.F("forward dial failed, %d", f.failures)
|
||||
}
|
||||
|
||||
return c, err
|
||||
}
|
||||
|
||||
// Failures returns the failuer count of forwarder
|
||||
func (f *Forwarder) Failures() uint32 {
|
||||
return atomic.LoadUint32(&f.failures)
|
||||
}
|
||||
|
||||
// Enable .
|
||||
func (f *Forwarder) Enable(b bool) {
|
||||
f.disabled = !b
|
||||
func (f *Forwarder) Enable() {
|
||||
atomic.StoreUint32(&f.failures, 0)
|
||||
atomic.StoreUint32(&f.failures, 0)
|
||||
}
|
||||
|
||||
// Disable .
|
||||
func (f *Forwarder) Disable() {
|
||||
atomic.StoreUint32(&f.failures, 1)
|
||||
}
|
||||
|
||||
// Enabled .
|
||||
func (f *Forwarder) Enabled() bool {
|
||||
return !f.disabled
|
||||
return !isTrue(atomic.LoadUint32(&f.disabled))
|
||||
}
|
||||
|
||||
func isTrue(n uint32) bool {
|
||||
return n&1 == 1
|
||||
}
|
||||
|
@ -1,90 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"net"
|
||||
"net/textproto"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("http", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*HTTP
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewHTTP(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{HTTP: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("[http] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write([]byte("CONNECT " + addr + " HTTP/1.1\r\n"))
|
||||
buf.Write([]byte("Proxy-Connection: Keep-Alive\r\n"))
|
||||
|
||||
if s.user != "" && s.password != "" {
|
||||
auth := s.user + ":" + s.password
|
||||
buf.Write([]byte("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + "\r\n"))
|
||||
}
|
||||
|
||||
//header ended
|
||||
buf.Write([]byte("\r\n"))
|
||||
rc.Write(buf.Bytes())
|
||||
|
||||
respR := bufio.NewReader(rc)
|
||||
respTP := textproto.NewReader(respR)
|
||||
_, code, _, ok := parseFirstLine(respTP)
|
||||
if ok && code == "200" {
|
||||
return rc, err
|
||||
} else if code == "407" {
|
||||
log.F("[http] authencation needed by proxy %s", s.addr)
|
||||
} else if code == "405" {
|
||||
log.F("[http] 'CONNECT' method not allowed by proxy %s", s.addr)
|
||||
}
|
||||
|
||||
return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code)
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||
return nil, nil, errors.New("http client does not support udp")
|
||||
}
|
@ -4,23 +4,38 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/textproto"
|
||||
"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 proxy.Dialer
|
||||
addr string
|
||||
user string
|
||||
password string
|
||||
}
|
||||
|
||||
// NewHTTP returns a http base struct
|
||||
func NewHTTP(s string) (*HTTP, error) {
|
||||
func init() {
|
||||
proxy.RegisterDialer("http", NewHTTPDialer)
|
||||
proxy.RegisterServer("http", NewHTTPServer)
|
||||
}
|
||||
|
||||
// NewHTTP returns a http proxy.
|
||||
func NewHTTP(s string, dialer proxy.Dialer) (*HTTP, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -32,6 +47,7 @@ func NewHTTP(s string) (*HTTP, error) {
|
||||
pass, _ := u.User.Password()
|
||||
|
||||
h := &HTTP{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
user: user,
|
||||
password: pass,
|
||||
@ -40,12 +56,215 @@ func NewHTTP(s string) (*HTTP, error) {
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// NewHTTPDialer returns a http proxy dialer.
|
||||
func NewHTTPDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewHTTP(s, dialer)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.F("failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
log.F("listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[http] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.Serve(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve .
|
||||
func (s *HTTP) Serve(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
reqR := bufio.NewReader(c)
|
||||
reqTP := textproto.NewReader(reqR)
|
||||
method, requestURI, proto, ok := parseFirstLine(reqTP)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if method == "CONNECT" {
|
||||
s.servHTTPS(method, requestURI, proto, c)
|
||||
return
|
||||
}
|
||||
|
||||
reqHeader, err := reqTP.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
log.F("read header error:%s", err)
|
||||
return
|
||||
}
|
||||
cleanHeaders(reqHeader)
|
||||
|
||||
// tell the remote server not to keep alive
|
||||
reqHeader.Set("Connection", "close")
|
||||
|
||||
u, err := url.ParseRequestURI(requestURI)
|
||||
if err != nil {
|
||||
log.F("[http] parse request url error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
var tgt = u.Host
|
||||
if !strings.Contains(u.Host, ":") {
|
||||
tgt += ":80"
|
||||
}
|
||||
|
||||
rc, err := s.dialer.Dial("tcp", tgt)
|
||||
if err != nil {
|
||||
fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto)
|
||||
log.F("[http] failed to dial: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
// GET http://example.com/a/index.htm HTTP/1.1 -->
|
||||
// GET /a/index.htm HTTP/1.1
|
||||
u.Scheme = ""
|
||||
u.Host = ""
|
||||
uri := u.String()
|
||||
|
||||
var reqBuf bytes.Buffer
|
||||
writeFirstLine(method, uri, proto, &reqBuf)
|
||||
writeHeaders(reqHeader, &reqBuf)
|
||||
|
||||
// send request to remote server
|
||||
rc.Write(reqBuf.Bytes())
|
||||
|
||||
// copy the left request bytes to remote server. eg. length specificed or chunked body.
|
||||
go func() {
|
||||
if _, err := reqR.Peek(1); err == nil {
|
||||
io.Copy(rc, reqR)
|
||||
rc.SetDeadline(time.Now())
|
||||
c.SetDeadline(time.Now())
|
||||
}
|
||||
}()
|
||||
|
||||
respR := bufio.NewReader(rc)
|
||||
respTP := textproto.NewReader(respR)
|
||||
proto, code, status, ok := parseFirstLine(respTP)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
respHeader, err := respTP.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
log.F("[http] read header error:%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
respHeader.Set("Proxy-Connection", "close")
|
||||
respHeader.Set("Connection", "close")
|
||||
|
||||
var respBuf bytes.Buffer
|
||||
writeFirstLine(proto, code, status, &respBuf)
|
||||
writeHeaders(respHeader, &respBuf)
|
||||
|
||||
log.F("[http] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
c.Write(respBuf.Bytes())
|
||||
|
||||
io.Copy(c, respR)
|
||||
|
||||
}
|
||||
|
||||
func (s *HTTP) servHTTPS(method, requestURI, proto string, c net.Conn) {
|
||||
rc, err := s.dialer.Dial("tcp", requestURI)
|
||||
if err != nil {
|
||||
c.Write([]byte(proto))
|
||||
c.Write([]byte(" 502 ERROR\r\n\r\n"))
|
||||
log.F("[http] failed to dial: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
|
||||
|
||||
log.F("[http] %s <-> %s [c]", c.RemoteAddr(), requestURI)
|
||||
|
||||
_, _, 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)
|
||||
}
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *HTTP) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
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 {
|
||||
log.F("[http] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.Write([]byte("CONNECT " + addr + " HTTP/1.1\r\n"))
|
||||
buf.Write([]byte("Proxy-Connection: Keep-Alive\r\n"))
|
||||
|
||||
if s.user != "" && s.password != "" {
|
||||
auth := s.user + ":" + s.password
|
||||
buf.Write([]byte("Proxy-Authorization: Basic " + base64.StdEncoding.EncodeToString([]byte(auth)) + "\r\n"))
|
||||
}
|
||||
|
||||
//header ended
|
||||
buf.Write([]byte("\r\n"))
|
||||
rc.Write(buf.Bytes())
|
||||
|
||||
respR := bufio.NewReader(rc)
|
||||
respTP := textproto.NewReader(respR)
|
||||
_, code, _, ok := parseFirstLine(respTP)
|
||||
if ok && code == "200" {
|
||||
return rc, err
|
||||
} else if code == "407" {
|
||||
log.F("[http] authencation needed by proxy %s", s.addr)
|
||||
} else if code == "405" {
|
||||
log.F("[http] 'CONNECT' method not allowed by proxy %s", s.addr)
|
||||
}
|
||||
|
||||
return nil, errors.New("[http] can not connect remote address: " + addr + ". error code: " + code)
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *HTTP) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||
return nil, nil, errors.New("http client does not support udp")
|
||||
}
|
||||
|
||||
// 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()
|
||||
// log.F("first line: %s", line)
|
||||
if err != nil {
|
||||
log.F("[http] read first line error: %s", err)
|
||||
log.F("[http] read first line error:%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,184 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("http", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*HTTP
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewHTTP(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{HTTP: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) ListenAndServe() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
log.F("listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[http] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.Serve(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve .
|
||||
func (s *Server) Serve(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
reqR := bufio.NewReader(c)
|
||||
reqTP := textproto.NewReader(reqR)
|
||||
method, requestURI, proto, ok := parseFirstLine(reqTP)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if method == "CONNECT" {
|
||||
s.servHTTPS(method, requestURI, proto, c)
|
||||
return
|
||||
}
|
||||
|
||||
reqHeader, err := reqTP.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
log.F("read header error:%s", err)
|
||||
return
|
||||
}
|
||||
cleanHeaders(reqHeader)
|
||||
|
||||
// tell the remote server not to keep alive
|
||||
reqHeader.Set("Connection", "close")
|
||||
|
||||
u, err := url.ParseRequestURI(requestURI)
|
||||
if err != nil {
|
||||
log.F("[http] parse request url error: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
var tgt = u.Host
|
||||
if !strings.Contains(u.Host, ":") {
|
||||
tgt += ":80"
|
||||
}
|
||||
|
||||
rc, err := s.Dial("tcp", tgt)
|
||||
if err != nil {
|
||||
fmt.Fprintf(c, "%s 502 ERROR\r\n\r\n", proto)
|
||||
log.F("[http] failed to dial: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
// GET http://example.com/a/index.htm HTTP/1.1 -->
|
||||
// GET /a/index.htm HTTP/1.1
|
||||
u.Scheme = ""
|
||||
u.Host = ""
|
||||
uri := u.String()
|
||||
|
||||
var reqBuf bytes.Buffer
|
||||
writeFirstLine(method, uri, proto, &reqBuf)
|
||||
writeHeaders(reqHeader, &reqBuf)
|
||||
|
||||
// send request to remote server
|
||||
rc.Write(reqBuf.Bytes())
|
||||
|
||||
// copy the left request bytes to remote server. eg. length specificed or chunked body.
|
||||
go func() {
|
||||
if _, err := reqR.Peek(1); err == nil {
|
||||
io.Copy(rc, reqR)
|
||||
rc.SetDeadline(time.Now())
|
||||
c.SetDeadline(time.Now())
|
||||
}
|
||||
}()
|
||||
|
||||
respR := bufio.NewReader(rc)
|
||||
respTP := textproto.NewReader(respR)
|
||||
proto, code, status, ok := parseFirstLine(respTP)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
respHeader, err := respTP.ReadMIMEHeader()
|
||||
if err != nil {
|
||||
log.F("[http] read header error:%s", err)
|
||||
return
|
||||
}
|
||||
|
||||
respHeader.Set("Proxy-Connection", "close")
|
||||
respHeader.Set("Connection", "close")
|
||||
|
||||
var respBuf bytes.Buffer
|
||||
writeFirstLine(proto, code, status, &respBuf)
|
||||
writeHeaders(respHeader, &respBuf)
|
||||
|
||||
log.F("[http] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
c.Write(respBuf.Bytes())
|
||||
|
||||
io.Copy(c, respR)
|
||||
|
||||
}
|
||||
|
||||
func (s *Server) servHTTPS(method, requestURI, proto string, c net.Conn) {
|
||||
rc, err := s.Dial("tcp", requestURI)
|
||||
if err != nil {
|
||||
c.Write([]byte(proto))
|
||||
c.Write([]byte(" 502 ERROR\r\n\r\n"))
|
||||
log.F("[http] failed to dial: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
c.Write([]byte("HTTP/1.1 200 Connection established\r\n\r\n"))
|
||||
|
||||
log.F("[http] %s <-> %s [c]", c.RemoteAddr(), requestURI)
|
||||
|
||||
_, _, 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)
|
||||
}
|
||||
}
|
@ -12,10 +12,6 @@ import (
|
||||
"github.com/nadoo/glider/proxy/socks5"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("mixed", NewMixedProxyServer)
|
||||
}
|
||||
|
||||
// https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase.
|
||||
var httpMethods = [...][]byte{
|
||||
[]byte("GET"),
|
||||
@ -28,40 +24,45 @@ var httpMethods = [...][]byte{
|
||||
[]byte("TRACE"),
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*proxy.Forwarder
|
||||
// MixedProxy struct
|
||||
type MixedProxy struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
http *http.Server
|
||||
socks5 *socks5.Server
|
||||
|
||||
http *http.HTTP
|
||||
socks5 *socks5.SOCKS5
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("mixed", NewMixedProxyServer)
|
||||
}
|
||||
|
||||
// NewMixedProxy returns a mixed proxy.
|
||||
func NewMixedProxy(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
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 := &Server{
|
||||
Forwarder: f,
|
||||
addr: u.Host,
|
||||
p := &MixedProxy{
|
||||
dialer: dialer,
|
||||
addr: u.Host,
|
||||
}
|
||||
|
||||
p.http, _ = http.NewServer(s, f)
|
||||
p.socks5, _ = socks5.NewServer(s, f)
|
||||
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, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewMixedProxy(s, f)
|
||||
func NewMixedProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewMixedProxy(s, dialer)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (p *Server) ListenAndServe() {
|
||||
func (p *MixedProxy) ListenAndServe() {
|
||||
|
||||
go p.socks5.ListenAndServeUDP()
|
||||
|
||||
@ -85,7 +86,7 @@ func (p *Server) ListenAndServe() {
|
||||
}
|
||||
|
||||
// Serve .
|
||||
func (p *Server) Serve(c net.Conn) {
|
||||
func (p *MixedProxy) Serve(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
@ -16,10 +16,6 @@ import (
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("redir", CreateServer)
|
||||
}
|
||||
|
||||
const (
|
||||
// SO_ORIGINAL_DST from linux/include/uapi/linux/netfilter_ipv4.h
|
||||
SO_ORIGINAL_DST = 80
|
||||
@ -27,31 +23,40 @@ const (
|
||||
IP6T_SO_ORIGINAL_DST = 80
|
||||
)
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
addr string
|
||||
*proxy.Forwarder
|
||||
// RedirProxy struct
|
||||
type RedirProxy struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
func init() {
|
||||
proxy.RegisterServer("redir", NewRedirServer)
|
||||
}
|
||||
|
||||
// NewRedirProxy returns a redirect proxy.
|
||||
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
|
||||
}
|
||||
|
||||
server := &Server{addr: u.Host, Forwarder: f}
|
||||
return server, nil
|
||||
addr := u.Host
|
||||
r := &RedirProxy{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
// NewRedirServer returns a redir server.
|
||||
func NewRedirServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewRedirProxy(s, dialer)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (s *Server) ListenAndServe() {
|
||||
func (s *RedirProxy) ListenAndServe() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[redir] failed to listen on %s: %v", s.addr, err)
|
||||
@ -80,7 +85,7 @@ func (s *Server) ListenAndServe() {
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := s.Dial("tcp", tgt.String())
|
||||
rc, err := s.dialer.Dial("tcp", tgt.String())
|
||||
if err != nil {
|
||||
log.F("[redir] failed to connect to target: %v", err)
|
||||
return
|
@ -15,7 +15,7 @@ type Server interface {
|
||||
}
|
||||
|
||||
// ServerCreator is a function to create proxy servers.
|
||||
type ServerCreator func(s string, f *Forwarder) (Server, error)
|
||||
type ServerCreator func(s string, dialer Dialer) (Server, error)
|
||||
|
||||
var (
|
||||
serverMap = make(map[string]ServerCreator)
|
||||
@ -27,7 +27,7 @@ func RegisterServer(name string, c ServerCreator) {
|
||||
}
|
||||
|
||||
// ServerFromURL calls the registered creator to create proxy servers.
|
||||
func ServerFromURL(s string, f *Forwarder) (Server, error) {
|
||||
func ServerFromURL(s string, dialer Dialer) (Server, error) {
|
||||
if !strings.Contains(s, "://") {
|
||||
s = "mixed://" + s
|
||||
}
|
||||
@ -38,13 +38,13 @@ func ServerFromURL(s string, f *Forwarder) (Server, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if f == nil {
|
||||
f = NewForwarder(Direct)
|
||||
if dialer == nil {
|
||||
dialer = Direct
|
||||
}
|
||||
|
||||
c, ok := serverMap[strings.ToLower(u.Scheme)]
|
||||
if ok {
|
||||
return c(s, f)
|
||||
return c(s, dialer)
|
||||
}
|
||||
|
||||
return nil, errors.New("unknown scheme '" + u.Scheme + "'")
|
||||
|
@ -1,251 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("socks5", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*SOCKS5
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
socks, err := NewSOCKS5(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{SOCKS5: socks, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return nil, errors.New("[socks5]: no support for connection type " + network)
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.connect(c, addr); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (pc net.PacketConn, writeTo net.Addr, err error) {
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5] dialudp dial tcp to %s error: %s", s.addr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// send VER, NMETHODS, METHODS
|
||||
c.Write([]byte{5, 1, 0})
|
||||
|
||||
buf := make([]byte, socks.MaxAddrLen)
|
||||
// read VER METHOD
|
||||
if _, err := io.ReadFull(c, buf[:2]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
dstAddr := socks.ParseAddr(addr)
|
||||
// write VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||
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 {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rep := buf[1]
|
||||
if rep != 0 {
|
||||
log.F("[socks5] server reply: %d, not succeeded", rep)
|
||||
return nil, nil, errors.New("server connect failed")
|
||||
}
|
||||
|
||||
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 {
|
||||
log.F("[socks5] dialudp to %s error: %s", uAddr.String(), err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkc := NewPktConn(pc, nextHop, dstAddr, true, c)
|
||||
return pkc, nextHop, err
|
||||
}
|
||||
|
||||
// connect takes an existing connection to a socks5 proxy server,
|
||||
// and commands the server to extend that connection to target,
|
||||
// which must be a canonical address with a host and port.
|
||||
func (s *Dialer) connect(conn net.Conn, target string) error {
|
||||
host, portStr, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||
}
|
||||
if port < 1 || port > 0xffff {
|
||||
return errors.New("proxy: port number out of range: " + portStr)
|
||||
}
|
||||
|
||||
// the size here is just an estimate
|
||||
buf := make([]byte, 0, 6+len(host))
|
||||
|
||||
buf = append(buf, Version)
|
||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||
buf = append(buf, 2 /* num auth methods */, socks.AuthNone, socks.AuthPassword)
|
||||
} else {
|
||||
buf = append(buf, 1 /* num auth methods */, socks.AuthNone)
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if buf[1] == 0xff {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||
}
|
||||
|
||||
if buf[1] == socks.AuthPassword {
|
||||
buf = buf[:0]
|
||||
buf = append(buf, 1 /* password protocol version */)
|
||||
buf = append(buf, uint8(len(s.user)))
|
||||
buf = append(buf, s.user...)
|
||||
buf = append(buf, uint8(len(s.password)))
|
||||
buf = append(buf, s.password...)
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if buf[1] != 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:0]
|
||||
buf = append(buf, Version, socks.CmdConnect, 0 /* reserved */)
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, socks.ATypIP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, socks.ATypIP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination hostname too long: " + host)
|
||||
}
|
||||
buf = append(buf, socks.ATypDomain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
buf = append(buf, byte(port>>8), byte(port))
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(socks.Errors) {
|
||||
failure = socks.Errors[buf[1]].Error()
|
||||
}
|
||||
|
||||
if len(failure) > 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||
}
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case socks.ATypIP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case socks.ATypIP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case socks.ATypDomain:
|
||||
_, 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())
|
||||
}
|
||||
bytesToDiscard = int(buf[0])
|
||||
default:
|
||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||
}
|
||||
|
||||
if cap(buf) < bytesToDiscard {
|
||||
buf = make([]byte, bytesToDiscard)
|
||||
} else {
|
||||
buf = buf[:bytesToDiscard]
|
||||
}
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Also need to discard the port number
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,214 +0,0 @@
|
||||
package socks5
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"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"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("socks5", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*SOCKS5
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewSOCKS5(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{SOCKS5: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves socks5 requests.
|
||||
func (s *Server) ListenAndServe() {
|
||||
go s.ListenAndServeUDP()
|
||||
s.ListenAndServeTCP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP .
|
||||
func (s *Server) ListenAndServeTCP() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("[socks5] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.ServeTCP(c)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeTCP .
|
||||
func (s *Server) ServeTCP(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
tgt, err := s.handshake(c)
|
||||
if err != nil {
|
||||
// UDP: keep the connection until disconnect then free the UDP socket
|
||||
if err == socks.Errors[9] {
|
||||
buf := []byte{}
|
||||
// block here
|
||||
for {
|
||||
_, err := c.Read(buf)
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
continue
|
||||
}
|
||||
// log.F("[socks5] servetcp udp associate end")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.F("[socks5] failed to get target address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := s.Dial("tcp", tgt.String())
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[socks5] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("[socks5] relay error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAndServeUDP serves udp requests.
|
||||
func (s *Server) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
log.F("[socks5-udp] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
c := NewPktConn(lc, nil, nil, true, nil)
|
||||
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc *PktConn
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
if c.tgtAddr == nil {
|
||||
log.F("[socks5-udp] can not get target address, not a valid request")
|
||||
continue
|
||||
}
|
||||
|
||||
lpc, nextHop, err := s.DialUDP("udp", c.tgtAddr.String())
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pc = NewPktConn(lpc, nextHop, nil, false, nil)
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(*PktConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Handshake fast-tracks SOCKS initialization to get target address to connect.
|
||||
func (s *Server) handshake(rw io.ReadWriter) (socks.Addr, error) {
|
||||
// Read RFC 1928 for request and reply structure and sizes.
|
||||
buf := make([]byte, socks.MaxAddrLen)
|
||||
// read VER, NMETHODS, METHODS
|
||||
if _, err := io.ReadFull(rw, buf[:2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nmethods := buf[1]
|
||||
if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// write VER METHOD
|
||||
if _, err := rw.Write([]byte{5, 0}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// read VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||
if _, err := io.ReadFull(rw, buf[:3]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := buf[1]
|
||||
addr, err := socks.ReadAddrBuf(rw, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch cmd {
|
||||
case socks.CmdConnect:
|
||||
_, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded
|
||||
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, socks.Errors[7]
|
||||
}
|
||||
err = socks.Errors[9]
|
||||
default:
|
||||
return nil, socks.Errors[7]
|
||||
}
|
||||
|
||||
return addr, err // skip VER, CMD, RSV fields
|
||||
}
|
@ -12,9 +12,18 @@
|
||||
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"
|
||||
)
|
||||
|
||||
// Version is socks5 version number
|
||||
@ -22,14 +31,20 @@ const Version = 5
|
||||
|
||||
// SOCKS5 struct
|
||||
type SOCKS5 struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
user string
|
||||
password string
|
||||
}
|
||||
|
||||
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(s string) (*SOCKS5, error) {
|
||||
func NewSOCKS5(s string, dialer proxy.Dialer) (*SOCKS5, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -41,6 +56,7 @@ func NewSOCKS5(s string) (*SOCKS5, error) {
|
||||
pass, _ := u.User.Password()
|
||||
|
||||
h := &SOCKS5{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
user: user,
|
||||
password: pass,
|
||||
@ -48,3 +64,402 @@ func NewSOCKS5(s string) (*SOCKS5, error) {
|
||||
|
||||
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.
|
||||
func (s *SOCKS5) ListenAndServe() {
|
||||
go s.ListenAndServeUDP()
|
||||
s.ListenAndServeTCP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP .
|
||||
func (s *SOCKS5) ListenAndServeTCP() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("[socks5] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.ServeTCP(c)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeTCP .
|
||||
func (s *SOCKS5) ServeTCP(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
tgt, err := s.handshake(c)
|
||||
if err != nil {
|
||||
// UDP: keep the connection until disconnect then free the UDP socket
|
||||
if err == socks.Errors[9] {
|
||||
buf := []byte{}
|
||||
// block here
|
||||
for {
|
||||
_, err := c.Read(buf)
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
continue
|
||||
}
|
||||
// log.F("[socks5] servetcp udp associate end")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.F("[socks5] failed to get target address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
rc, err := s.dialer.Dial("tcp", tgt.String())
|
||||
if err != nil {
|
||||
log.F("[socks5] failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[socks5] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("[socks5] relay error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ListenAndServeUDP serves udp requests.
|
||||
func (s *SOCKS5) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
log.F("[socks5-udp] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
c := NewPktConn(lc, nil, nil, true, nil)
|
||||
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc *PktConn
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
if c.tgtAddr == nil {
|
||||
log.F("[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 {
|
||||
log.F("[socks5-udp] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pc = NewPktConn(lpc, nextHop, nil, false, nil)
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(*PktConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
|
||||
if err != nil {
|
||||
log.F("[socks5-udp] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[socks5-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *SOCKS5) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
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) {
|
||||
switch network {
|
||||
case "tcp", "tcp6", "tcp4":
|
||||
default:
|
||||
return nil, errors.New("[socks5]: no support for connection type " + network)
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := s.connect(c, addr); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
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 {
|
||||
log.F("[socks5] dialudp dial tcp to %s error: %s", s.addr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// send VER, NMETHODS, METHODS
|
||||
c.Write([]byte{5, 1, 0})
|
||||
|
||||
buf := make([]byte, socks.MaxAddrLen)
|
||||
// read VER METHOD
|
||||
if _, err := io.ReadFull(c, buf[:2]); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
dstAddr := socks.ParseAddr(addr)
|
||||
// write VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||
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 {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
rep := buf[1]
|
||||
if rep != 0 {
|
||||
log.F("[socks5] server reply: %d, not succeeded", rep)
|
||||
return nil, nil, errors.New("server connect failed")
|
||||
}
|
||||
|
||||
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 {
|
||||
log.F("[socks5] dialudp to %s error: %s", uAddr.String(), err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkc := NewPktConn(pc, nextHop, dstAddr, true, c)
|
||||
return pkc, nextHop, err
|
||||
}
|
||||
|
||||
// connect takes an existing connection to a socks5 proxy server,
|
||||
// and commands the server to extend that connection to target,
|
||||
// which must be a canonical address with a host and port.
|
||||
func (s *SOCKS5) connect(conn net.Conn, target string) error {
|
||||
host, portStr, err := net.SplitHostPort(target)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return errors.New("proxy: failed to parse port number: " + portStr)
|
||||
}
|
||||
if port < 1 || port > 0xffff {
|
||||
return errors.New("proxy: port number out of range: " + portStr)
|
||||
}
|
||||
|
||||
// the size here is just an estimate
|
||||
buf := make([]byte, 0, 6+len(host))
|
||||
|
||||
buf = append(buf, Version)
|
||||
if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 {
|
||||
buf = append(buf, 2 /* num auth methods */, socks.AuthNone, socks.AuthPassword)
|
||||
} else {
|
||||
buf = append(buf, 1 /* num auth methods */, socks.AuthNone)
|
||||
}
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
if buf[0] != 5 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0])))
|
||||
}
|
||||
if buf[1] == 0xff {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication")
|
||||
}
|
||||
|
||||
if buf[1] == socks.AuthPassword {
|
||||
buf = buf[:0]
|
||||
buf = append(buf, 1 /* password protocol version */)
|
||||
buf = append(buf, uint8(len(s.user)))
|
||||
buf = append(buf, s.user...)
|
||||
buf = append(buf, uint8(len(s.password)))
|
||||
buf = append(buf, s.password...)
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if buf[1] != 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password")
|
||||
}
|
||||
}
|
||||
|
||||
buf = buf[:0]
|
||||
buf = append(buf, Version, socks.CmdConnect, 0 /* reserved */)
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
buf = append(buf, socks.ATypIP4)
|
||||
ip = ip4
|
||||
} else {
|
||||
buf = append(buf, socks.ATypIP6)
|
||||
}
|
||||
buf = append(buf, ip...)
|
||||
} else {
|
||||
if len(host) > 255 {
|
||||
return errors.New("proxy: destination hostname too long: " + host)
|
||||
}
|
||||
buf = append(buf, socks.ATypDomain)
|
||||
buf = append(buf, byte(len(host)))
|
||||
buf = append(buf, host...)
|
||||
}
|
||||
buf = append(buf, byte(port>>8), byte(port))
|
||||
|
||||
if _, err := conn.Write(buf); err != nil {
|
||||
return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
if _, err := io.ReadFull(conn, buf[:4]); err != nil {
|
||||
return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
failure := "unknown error"
|
||||
if int(buf[1]) < len(socks.Errors) {
|
||||
failure = socks.Errors[buf[1]].Error()
|
||||
}
|
||||
|
||||
if len(failure) > 0 {
|
||||
return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure)
|
||||
}
|
||||
|
||||
bytesToDiscard := 0
|
||||
switch buf[3] {
|
||||
case socks.ATypIP4:
|
||||
bytesToDiscard = net.IPv4len
|
||||
case socks.ATypIP6:
|
||||
bytesToDiscard = net.IPv6len
|
||||
case socks.ATypDomain:
|
||||
_, 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())
|
||||
}
|
||||
bytesToDiscard = int(buf[0])
|
||||
default:
|
||||
return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr)
|
||||
}
|
||||
|
||||
if cap(buf) < bytesToDiscard {
|
||||
buf = make([]byte, bytesToDiscard)
|
||||
} else {
|
||||
buf = buf[:bytesToDiscard]
|
||||
}
|
||||
if _, err := io.ReadFull(conn, buf); err != nil {
|
||||
return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
// Also need to discard the port number
|
||||
if _, err := io.ReadFull(conn, buf[:2]); err != nil {
|
||||
return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handshake fast-tracks SOCKS initialization to get target address to connect.
|
||||
func (s *SOCKS5) handshake(rw io.ReadWriter) (socks.Addr, error) {
|
||||
// Read RFC 1928 for request and reply structure and sizes.
|
||||
buf := make([]byte, socks.MaxAddrLen)
|
||||
// read VER, NMETHODS, METHODS
|
||||
if _, err := io.ReadFull(rw, buf[:2]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nmethods := buf[1]
|
||||
if _, err := io.ReadFull(rw, buf[:nmethods]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// write VER METHOD
|
||||
if _, err := rw.Write([]byte{5, 0}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// read VER CMD RSV ATYP DST.ADDR DST.PORT
|
||||
if _, err := io.ReadFull(rw, buf[:3]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd := buf[1]
|
||||
addr, err := socks.ReadAddrBuf(rw, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch cmd {
|
||||
case socks.CmdConnect:
|
||||
_, err = rw.Write([]byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0}) // SOCKS v5, reply succeeded
|
||||
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, socks.Errors[7]
|
||||
}
|
||||
err = socks.Errors[9]
|
||||
default:
|
||||
return nil, socks.Errors[7]
|
||||
}
|
||||
|
||||
return addr, err // skip VER, CMD, RSV fields
|
||||
}
|
||||
|
@ -1,86 +0,0 @@
|
||||
package ss
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/common/socks"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ss", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*SS
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewSS(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{SS: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
target := socks.ParseAddr(addr)
|
||||
if target == nil {
|
||||
return nil, errors.New("[ss] unable to parse address: " + addr)
|
||||
}
|
||||
|
||||
if network == "uot" {
|
||||
target[0] = target[0] | 0x8
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c = s.StreamConn(c)
|
||||
if _, err = c.Write(target); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, err
|
||||
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
pc, nextHop, err := s.dialer.DialUDP(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] dialudp to %s error: %s", s.addr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkc := NewPktConn(s.PacketConn(pc), nextHop, socks.ParseAddr(addr), true)
|
||||
return pkc, nextHop, err
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
package ss
|
||||
|
||||
import (
|
||||
"net"
|
||||
"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"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("ss", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*SS
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewSS(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{SS: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) ListenAndServe() {
|
||||
go s.ListenAndServeUDP()
|
||||
s.ListenAndServeTCP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP serves tcp requests
|
||||
func (s *Server) ListenAndServeTCP() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("[ss] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[ss] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
go s.ServeTCP(c)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeTCP serves tcp requests
|
||||
func (s *Server) ServeTCP(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
c = s.StreamConn(c)
|
||||
|
||||
tgt, err := socks.ReadAddr(c)
|
||||
if err != nil {
|
||||
log.F("[ss] failed to get target address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dialer := s.NextDialer(tgt.String())
|
||||
|
||||
// udp over tcp?
|
||||
uot := socks.UoT(tgt[0])
|
||||
if uot && dialer.Addr() == "DIRECT" {
|
||||
rc, err := net.ListenPacket("udp", "")
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] UDP remote listen error: %v", err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
req := make([]byte, conn.UDPBufSize)
|
||||
n, err := c.Read(req)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] error in ioutil.ReadAll: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
tgtAddr, _ := net.ResolveUDPAddr("udp", tgt.String())
|
||||
rc.WriteTo(req[:n], tgtAddr)
|
||||
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
n, _, err = rc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] read error: %v", err)
|
||||
}
|
||||
|
||||
c.Write(buf[:n])
|
||||
|
||||
log.F("[ss] %s <-tcp-> %s - %s <-udp-> %s ", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
if uot {
|
||||
network = "udp"
|
||||
}
|
||||
|
||||
rc, err := dialer.Dial(network, tgt.String())
|
||||
if err != nil {
|
||||
log.F("[ss] failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[ss] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("[ss] relay error: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ListenAndServeUDP serves udp requests
|
||||
func (s *Server) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
lc = s.PacketConn(lc)
|
||||
|
||||
log.F("[ss-udp] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
c := NewPktConn(lc, nil, nil, true)
|
||||
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc *PktConn
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
lpc, nextHop, err := s.DialUDP("udp", c.tgtAddr.String())
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pc = NewPktConn(lpc, nextHop, nil, false)
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(*PktConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[ss-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
}
|
||||
}
|
236
proxy/ss/ss.go
236
proxy/ss/ss.go
@ -1,22 +1,36 @@
|
||||
package ss
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"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"
|
||||
)
|
||||
|
||||
// SS .
|
||||
type SS struct {
|
||||
addr string
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
core.Cipher
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ss", NewSSDialer)
|
||||
proxy.RegisterServer("ss", NewSSServer)
|
||||
}
|
||||
|
||||
// NewSS returns a shadowsocks proxy.
|
||||
func NewSS(s string) (*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)
|
||||
@ -33,6 +47,7 @@ func NewSS(s string) (*SS, error) {
|
||||
}
|
||||
|
||||
p := &SS{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
Cipher: ciph,
|
||||
}
|
||||
@ -40,7 +55,224 @@ func NewSS(s string) (*SS, error) {
|
||||
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.
|
||||
func (s *SS) ListenAndServe() {
|
||||
go s.ListenAndServeUDP()
|
||||
s.ListenAndServeTCP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP serves tcp ss requests.
|
||||
func (s *SS) ListenAndServeTCP() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.F("[ss] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[ss] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
go s.ServeTCP(c)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeTCP serves tcp ss requests.
|
||||
func (s *SS) ServeTCP(c net.Conn) {
|
||||
defer c.Close()
|
||||
|
||||
if c, ok := c.(*net.TCPConn); ok {
|
||||
c.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
c = s.StreamConn(c)
|
||||
|
||||
tgt, err := socks.ReadAddr(c)
|
||||
if err != nil {
|
||||
log.F("[ss] failed to get target address: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dialer := s.dialer.NextDialer(tgt.String())
|
||||
|
||||
// udp over tcp?
|
||||
uot := socks.UoT(tgt[0])
|
||||
if uot && dialer.Addr() == "DIRECT" {
|
||||
rc, err := net.ListenPacket("udp", "")
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] UDP remote listen error: %v", err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
req := make([]byte, conn.UDPBufSize)
|
||||
n, err := c.Read(req)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] error in ioutil.ReadAll: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
tgtAddr, _ := net.ResolveUDPAddr("udp", tgt.String())
|
||||
rc.WriteTo(req[:n], tgtAddr)
|
||||
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
n, _, err = rc.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-uottun] read error: %v", err)
|
||||
}
|
||||
|
||||
c.Write(buf[:n])
|
||||
|
||||
log.F("[ss] %s <-tcp-> %s - %s <-udp-> %s ", c.RemoteAddr(), c.LocalAddr(), rc.LocalAddr(), tgt)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
network := "tcp"
|
||||
if uot {
|
||||
network = "udp"
|
||||
}
|
||||
|
||||
rc, err := dialer.Dial(network, tgt.String())
|
||||
if err != nil {
|
||||
log.F("[ss] failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[ss] %s <-> %s", c.RemoteAddr(), tgt)
|
||||
|
||||
_, _, err = conn.Relay(c, rc)
|
||||
if err != nil {
|
||||
if err, ok := err.(net.Error); ok && err.Timeout() {
|
||||
return // ignore i/o timeout
|
||||
}
|
||||
log.F("[ss] relay error: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ListenAndServeUDP serves udp ss requests.
|
||||
func (s *SS) ListenAndServeUDP() {
|
||||
lc, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer lc.Close()
|
||||
|
||||
lc = s.PacketConn(lc)
|
||||
|
||||
log.F("[ss-udp] listening UDP on %s", s.addr)
|
||||
|
||||
var nm sync.Map
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
c := NewPktConn(lc, nil, nil, true)
|
||||
|
||||
n, raddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
var pc *PktConn
|
||||
v, ok := nm.Load(raddr.String())
|
||||
if !ok && v == nil {
|
||||
lpc, nextHop, err := s.dialer.DialUDP("udp", c.tgtAddr.String())
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote dial error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
pc = NewPktConn(lpc, nextHop, nil, false)
|
||||
nm.Store(raddr.String(), pc)
|
||||
|
||||
go func() {
|
||||
conn.TimedCopy(c, raddr, pc, 2*time.Minute)
|
||||
pc.Close()
|
||||
nm.Delete(raddr.String())
|
||||
}()
|
||||
|
||||
} else {
|
||||
pc = v.(*PktConn)
|
||||
}
|
||||
|
||||
_, err = pc.WriteTo(buf[:n], pc.writeAddr)
|
||||
if err != nil {
|
||||
log.F("[ss-udp] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[ss-udp] %s <-> %s", raddr, c.tgtAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// ListCipher .
|
||||
func ListCipher() string {
|
||||
return strings.Join(core.ListCipher(), " ")
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *SS) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
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 := socks.ParseAddr(addr)
|
||||
if target == nil {
|
||||
return nil, errors.New("[ss] unable to parse address: " + addr)
|
||||
}
|
||||
|
||||
if network == "uot" {
|
||||
target[0] = target[0] | 0x8
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c = s.StreamConn(c)
|
||||
if _, err = c.Write(target); err != nil {
|
||||
c.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c, err
|
||||
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *SS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
pc, nextHop, err := s.dialer.DialUDP(network, s.addr)
|
||||
if err != nil {
|
||||
log.F("[ss] dialudp to %s error: %s", s.addr, err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pkc := NewPktConn(s.PacketConn(pc), nextHop, socks.ParseAddr(addr), true)
|
||||
return pkc, nextHop, err
|
||||
}
|
||||
|
@ -1,130 +0,0 @@
|
||||
package ssr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
shadowsocksr "github.com/sun8911879/shadowsocksR"
|
||||
"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"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ssr", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*SSR
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewSSR(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{SSR: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
target := socks.ParseAddr(addr)
|
||||
if target == nil {
|
||||
return nil, errors.New("[ssr] unable to parse address: " + addr)
|
||||
}
|
||||
|
||||
cipher, err := shadowsocksr.NewStreamCipher(s.EncryptMethod, s.EncryptPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ssr] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ssrconn := shadowsocksr.NewSSTCPConn(c, cipher)
|
||||
if ssrconn.Conn == nil || ssrconn.RemoteAddr() == nil {
|
||||
return nil, errors.New("[ssr] nil connection")
|
||||
}
|
||||
|
||||
// should initialize obfs/protocol now
|
||||
rs := strings.Split(ssrconn.RemoteAddr().String(), ":")
|
||||
port, _ := strconv.Atoi(rs[1])
|
||||
|
||||
ssrconn.IObfs = obfs.NewObfs(s.Obfs)
|
||||
if ssrconn.IObfs == nil {
|
||||
return nil, errors.New("[ssr] unsupported obfs type: " + s.Obfs)
|
||||
}
|
||||
|
||||
obfsServerInfo := &ssr.ServerInfoForObfs{
|
||||
Host: rs[0],
|
||||
Port: uint16(port),
|
||||
TcpMss: 1460,
|
||||
Param: s.ObfsParam,
|
||||
}
|
||||
ssrconn.IObfs.SetServerInfo(obfsServerInfo)
|
||||
|
||||
ssrconn.IProtocol = protocol.NewProtocol(s.Protocol)
|
||||
if ssrconn.IProtocol == nil {
|
||||
return nil, errors.New("[ssr] unsupported protocol type: " + s.Protocol)
|
||||
}
|
||||
|
||||
protocolServerInfo := &ssr.ServerInfoForObfs{
|
||||
Host: rs[0],
|
||||
Port: uint16(port),
|
||||
TcpMss: 1460,
|
||||
Param: s.ProtocolParam,
|
||||
}
|
||||
ssrconn.IProtocol.SetServerInfo(protocolServerInfo)
|
||||
|
||||
if s.ObfsData == nil {
|
||||
s.ObfsData = ssrconn.IObfs.GetData()
|
||||
}
|
||||
ssrconn.IObfs.SetData(s.ObfsData)
|
||||
|
||||
if s.ProtocolData == nil {
|
||||
s.ProtocolData = ssrconn.IProtocol.GetData()
|
||||
}
|
||||
ssrconn.IProtocol.SetData(s.ProtocolData)
|
||||
|
||||
if _, err := ssrconn.Write(target); err != nil {
|
||||
ssrconn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ssrconn, err
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("[ssr] udp not supported now")
|
||||
}
|
113
proxy/ssr/ssr.go
113
proxy/ssr/ssr.go
@ -1,14 +1,26 @@
|
||||
package ssr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
shadowsocksr "github.com/sun8911879/shadowsocksR"
|
||||
"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 {
|
||||
addr string
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
EncryptMethod string
|
||||
EncryptPassword string
|
||||
@ -20,8 +32,12 @@ type SSR struct {
|
||||
ProtocolData interface{}
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ssr", NewSSRDialer)
|
||||
}
|
||||
|
||||
// NewSSR returns a shadowsocksr proxy, ssr://method:pass@host:port/query
|
||||
func NewSSR(s string) (*SSR, error) {
|
||||
func NewSSR(s string, dialer proxy.Dialer) (*SSR, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -33,6 +49,7 @@ func NewSSR(s string) (*SSR, error) {
|
||||
pass, _ := u.User.Password()
|
||||
|
||||
p := &SSR{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
EncryptMethod: method,
|
||||
EncryptPassword: pass,
|
||||
@ -46,3 +63,95 @@ func NewSSR(s string) (*SSR, error) {
|
||||
|
||||
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 {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
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 := socks.ParseAddr(addr)
|
||||
if target == nil {
|
||||
return nil, errors.New("[ssr] unable to parse address: " + addr)
|
||||
}
|
||||
|
||||
cipher, err := shadowsocksr.NewStreamCipher(s.EncryptMethod, s.EncryptPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[ssr] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ssrconn := shadowsocksr.NewSSTCPConn(c, cipher)
|
||||
if ssrconn.Conn == nil || ssrconn.RemoteAddr() == nil {
|
||||
return nil, errors.New("[ssr] nil connection")
|
||||
}
|
||||
|
||||
// should initialize obfs/protocol now
|
||||
rs := strings.Split(ssrconn.RemoteAddr().String(), ":")
|
||||
port, _ := strconv.Atoi(rs[1])
|
||||
|
||||
ssrconn.IObfs = obfs.NewObfs(s.Obfs)
|
||||
if ssrconn.IObfs == nil {
|
||||
return nil, errors.New("[ssr] unsupported obfs type: " + s.Obfs)
|
||||
}
|
||||
|
||||
obfsServerInfo := &ssr.ServerInfoForObfs{
|
||||
Host: rs[0],
|
||||
Port: uint16(port),
|
||||
TcpMss: 1460,
|
||||
Param: s.ObfsParam,
|
||||
}
|
||||
ssrconn.IObfs.SetServerInfo(obfsServerInfo)
|
||||
|
||||
ssrconn.IProtocol = protocol.NewProtocol(s.Protocol)
|
||||
if ssrconn.IProtocol == nil {
|
||||
return nil, errors.New("[ssr] unsupported protocol type: " + s.Protocol)
|
||||
}
|
||||
|
||||
protocolServerInfo := &ssr.ServerInfoForObfs{
|
||||
Host: rs[0],
|
||||
Port: uint16(port),
|
||||
TcpMss: 1460,
|
||||
Param: s.ProtocolParam,
|
||||
}
|
||||
ssrconn.IProtocol.SetServerInfo(protocolServerInfo)
|
||||
|
||||
if s.ObfsData == nil {
|
||||
s.ObfsData = ssrconn.IObfs.GetData()
|
||||
}
|
||||
ssrconn.IObfs.SetData(s.ObfsData)
|
||||
|
||||
if s.ProtocolData == nil {
|
||||
s.ProtocolData = ssrconn.IProtocol.GetData()
|
||||
}
|
||||
ssrconn.IProtocol.SetData(s.ProtocolData)
|
||||
|
||||
if _, err := ssrconn.Write(target); err != nil {
|
||||
ssrconn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ssrconn, err
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *SSR) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("[ssr] udp not supported now")
|
||||
}
|
||||
|
@ -1,80 +0,0 @@
|
||||
package tcptun
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("tcptun", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*TCPTun
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewTCPTun(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{TCPTun: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) 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.Dial("tcp", s.raddr)
|
||||
if err != nil {
|
||||
|
||||
log.F("failed to connect to target: %v", err)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
log.F("[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)
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
}
|
@ -1,20 +1,29 @@
|
||||
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 {
|
||||
addr string
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
raddr string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("tcptun", NewTCPTunServer)
|
||||
}
|
||||
|
||||
// NewTCPTun returns a tcptun proxy.
|
||||
func NewTCPTun(s string) (*TCPTun, error) {
|
||||
func NewTCPTun(s string, dialer proxy.Dialer) (*TCPTun, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -25,9 +34,61 @@ func NewTCPTun(s string) (*TCPTun, error) {
|
||||
d := strings.Split(addr, "=")
|
||||
|
||||
p := &TCPTun{
|
||||
addr: d[0],
|
||||
raddr: d[1],
|
||||
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("[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)
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
@ -1,65 +0,0 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
stdtls "crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("tls", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*TLS
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewTLS(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{TLS: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string { return s.addr }
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
cc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[tls] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := &stdtls.Config{
|
||||
ServerName: s.serverName,
|
||||
InsecureSkipVerify: s.skipVerify,
|
||||
}
|
||||
|
||||
c := stdtls.Client(cc, conf)
|
||||
err = c.Handshake()
|
||||
return c, err
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("tls client does not support udp now")
|
||||
}
|
@ -1,22 +1,31 @@
|
||||
package tls
|
||||
|
||||
import (
|
||||
stdtls "crypto/tls"
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// TLS .
|
||||
type TLS struct {
|
||||
addr string
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
serverName string
|
||||
skipVerify bool
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("tls", NewTLSDialer)
|
||||
}
|
||||
|
||||
// NewTLS returns a tls proxy.
|
||||
func NewTLS(s string) (*TLS, error) {
|
||||
func NewTLS(s string, dialer proxy.Dialer) (*TLS, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse url err: %s", err)
|
||||
@ -35,6 +44,7 @@ func NewTLS(s string) (*TLS, error) {
|
||||
serverName := addr[:colonPos]
|
||||
|
||||
p := &TLS{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
serverName: serverName,
|
||||
skipVerify: false,
|
||||
@ -46,3 +56,37 @@ func NewTLS(s string) (*TLS, error) {
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewTLSDialer returns a tls proxy dialer.
|
||||
func NewTLSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewTLS(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *TLS) Addr() string { return s.addr }
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *TLS) 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 *TLS) Dial(network, addr string) (net.Conn, error) {
|
||||
cc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[tls] dial to %s error: %s", s.addr, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := &stdtls.Config{
|
||||
ServerName: s.serverName,
|
||||
InsecureSkipVerify: s.skipVerify,
|
||||
}
|
||||
|
||||
c := stdtls.Client(cc, conf)
|
||||
err = c.Handshake()
|
||||
return c, err
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *TLS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("tls client does not support udp now")
|
||||
}
|
||||
|
@ -17,18 +17,18 @@ import (
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// TProxy struct
|
||||
type TProxy struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("tproxy", CreateServer)
|
||||
proxy.RegisterServer("tproxy", NewTProxyServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
addr string
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
// NewTProxy returns a tproxy.
|
||||
func NewTProxy(s string, dialer proxy.Dialer) (*TProxy, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -37,32 +37,32 @@ func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
|
||||
addr := u.Host
|
||||
|
||||
p := &Server{
|
||||
addr: addr,
|
||||
Forwarder: f,
|
||||
p := &TProxy{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
// NewTProxyServer returns a udp tunnel server.
|
||||
func NewTProxyServer(s string, dialer proxy.Dialer) (proxy.Server, error) {
|
||||
return NewTProxy(s, dialer)
|
||||
}
|
||||
|
||||
// ListenAndServe .
|
||||
func (s *Server) ListenAndServe() {
|
||||
func (s *TProxy) ListenAndServe() {
|
||||
// go s.ListenAndServeTCP()
|
||||
s.ListenAndServeUDP()
|
||||
}
|
||||
|
||||
// ListenAndServeTCP .
|
||||
func (s *Server) ListenAndServeTCP() {
|
||||
func (s *TProxy) ListenAndServeTCP() {
|
||||
log.F("[tproxy] tcp mode not supported now, please use 'redir' instead")
|
||||
}
|
||||
|
||||
// ListenAndServeUDP .
|
||||
func (s *Server) ListenAndServeUDP() {
|
||||
func (s *TProxy) ListenAndServeUDP() {
|
||||
laddr, err := net.ResolveUDPAddr("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[tproxy] failed to resolve addr %s: %v", s.addr, err)
|
@ -1,92 +0,0 @@
|
||||
package udptun
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("udptun", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*UDPTun
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewUDPTun(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{UDPTun: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) ListenAndServe() {
|
||||
c, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[udptun] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
log.F("[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("[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.DialUDP("udp", s.raddr)
|
||||
if err != nil {
|
||||
log.F("[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("[udptun] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[udptun] %s <-> %s", raddr, s.raddr)
|
||||
|
||||
}
|
||||
}
|
@ -1,20 +1,31 @@
|
||||
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 {
|
||||
addr string
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
raddr string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("udptun", NewUDPTunServer)
|
||||
}
|
||||
|
||||
// NewUDPTun returns a UDPTun proxy.
|
||||
func NewUDPTun(s string) (*UDPTun, error) {
|
||||
func NewUDPTun(s string, dialer proxy.Dialer) (*UDPTun, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -25,9 +36,71 @@ func NewUDPTun(s string) (*UDPTun, error) {
|
||||
d := strings.Split(addr, "=")
|
||||
|
||||
p := &UDPTun{
|
||||
addr: d[0],
|
||||
raddr: d[1],
|
||||
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("[udptun] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
log.F("[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("[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("[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("[udptun] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[udptun] %s <-> %s", raddr, s.raddr)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,90 +0,0 @@
|
||||
package uottun
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/common/conn"
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("uottun", CreateServer)
|
||||
}
|
||||
|
||||
// Server struct
|
||||
type Server struct {
|
||||
*UoTTun
|
||||
*proxy.Forwarder
|
||||
}
|
||||
|
||||
// NewServer returns a local proxy server
|
||||
func NewServer(s string, f *proxy.Forwarder) (*Server, error) {
|
||||
h, err := NewUoTTun(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server := &Server{UoTTun: h, Forwarder: f}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
// CreateServer returns a local proxy server
|
||||
func CreateServer(s string, f *proxy.Forwarder) (proxy.Server, error) {
|
||||
return NewServer(s, f)
|
||||
}
|
||||
|
||||
// ListenAndServe serves requests from clients
|
||||
func (s *Server) ListenAndServe() {
|
||||
c, err := net.ListenPacket("udp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[uottun] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
log.F("[uottun] listening UDP on %s", s.addr)
|
||||
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
n, clientAddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[uottun] read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
rc, err := s.Dial("uot", s.raddr)
|
||||
if err != nil {
|
||||
log.F("[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("[uottun] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[uottun] %s <-> %s", clientAddr, s.raddr)
|
||||
}
|
||||
}
|
@ -1,20 +1,31 @@
|
||||
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 {
|
||||
addr string
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
raddr string
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("uottun", NewUoTTunServer)
|
||||
}
|
||||
|
||||
// NewUoTTun returns a UoTTun proxy.
|
||||
func NewUoTTun(s string) (*UoTTun, error) {
|
||||
func NewUoTTun(s string, dialer proxy.Dialer) (*UoTTun, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse err: %s", err)
|
||||
@ -25,9 +36,69 @@ func NewUoTTun(s string) (*UoTTun, error) {
|
||||
d := strings.Split(addr, "=")
|
||||
|
||||
p := &UoTTun{
|
||||
addr: d[0],
|
||||
raddr: d[1],
|
||||
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("[uottun] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
log.F("[uottun] listening UDP on %s", s.addr)
|
||||
|
||||
buf := make([]byte, conn.UDPBufSize)
|
||||
|
||||
for {
|
||||
n, clientAddr, err := c.ReadFrom(buf)
|
||||
if err != nil {
|
||||
log.F("[uottun] read error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
rc, err := s.dialer.Dial("uot", s.raddr)
|
||||
if err != nil {
|
||||
log.F("[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("[uottun] remote write error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.F("[uottun] %s <-> %s", clientAddr, s.raddr)
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("vmess", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*VMess
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewVMess(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{VMess: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.NewConn(rc, addr)
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("vmess client does not support udp now")
|
||||
}
|
@ -1,15 +1,19 @@
|
||||
package vmess
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/nadoo/glider/common/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
// VMess .
|
||||
type VMess struct {
|
||||
addr string
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
uuid string
|
||||
alterID int
|
||||
@ -18,8 +22,12 @@ type VMess struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("vmess", NewVMessDialer)
|
||||
}
|
||||
|
||||
// NewVMess returns a vmess proxy.
|
||||
func NewVMess(s string) (*VMess, error) {
|
||||
func NewVMess(s string, dialer proxy.Dialer) (*VMess, error) {
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
log.F("parse url err: %s", err)
|
||||
@ -54,6 +62,7 @@ func NewVMess(s string) (*VMess, error) {
|
||||
}
|
||||
|
||||
p := &VMess{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
uuid: uuid,
|
||||
alterID: int(alterID),
|
||||
@ -63,3 +72,34 @@ func NewVMess(s string) (*VMess, error) {
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewVMessDialer returns a vmess proxy dialer.
|
||||
func NewVMessDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewVMess(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *VMess) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *VMess) 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 *VMess) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.NewConn(rc, addr)
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *VMess) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("vmess client does not support udp now")
|
||||
}
|
||||
|
@ -1,60 +0,0 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ws", CreateDialer)
|
||||
}
|
||||
|
||||
// Dialer struct
|
||||
type Dialer struct {
|
||||
*WS
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
// NewDialer returns a proxy dialer
|
||||
func NewDialer(s string, dialer proxy.Dialer) (*Dialer, error) {
|
||||
h, err := NewWS(s, dialer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := &Dialer{WS: h, dialer: dialer}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// CreateDialer returns a proxy dialer
|
||||
func CreateDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewDialer(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns dialer's address
|
||||
func (s *Dialer) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *Dialer) NextDialer(dstAddr string) proxy.Dialer { return s.dialer.NextDialer(dstAddr) }
|
||||
|
||||
// Dial establishes a connection to the addr
|
||||
func (s *Dialer) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.NewConn(rc, addr)
|
||||
}
|
||||
|
||||
// DialUDP returns a PacketConn to the addr
|
||||
func (s *Dialer) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("ws client does not support udp now")
|
||||
}
|
@ -1,6 +1,8 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
@ -10,10 +12,16 @@ import (
|
||||
|
||||
// WS .
|
||||
type WS struct {
|
||||
dialer proxy.Dialer
|
||||
addr string
|
||||
|
||||
client *Client
|
||||
}
|
||||
|
||||
func init() {
|
||||
proxy.RegisterDialer("ws", NewWSDialer)
|
||||
}
|
||||
|
||||
// NewWS returns a websocket proxy.
|
||||
func NewWS(s string, dialer proxy.Dialer) (*WS, error) {
|
||||
u, err := url.Parse(s)
|
||||
@ -42,9 +50,41 @@ func NewWS(s string, dialer proxy.Dialer) (*WS, error) {
|
||||
}
|
||||
|
||||
p := &WS{
|
||||
dialer: dialer,
|
||||
addr: addr,
|
||||
client: client,
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// NewWSDialer returns a ws proxy dialer.
|
||||
func NewWSDialer(s string, dialer proxy.Dialer) (proxy.Dialer, error) {
|
||||
return NewWS(s, dialer)
|
||||
}
|
||||
|
||||
// Addr returns forwarder's address
|
||||
func (s *WS) Addr() string {
|
||||
if s.addr == "" {
|
||||
return s.dialer.Addr()
|
||||
}
|
||||
return s.addr
|
||||
}
|
||||
|
||||
// NextDialer returns the next dialer
|
||||
func (s *WS) 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 *WS) Dial(network, addr string) (net.Conn, error) {
|
||||
rc, err := s.dialer.Dial("tcp", s.addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return s.client.NewConn(rc, addr)
|
||||
}
|
||||
|
||||
// DialUDP connects to the given address via the proxy.
|
||||
func (s *WS) DialUDP(network, addr string) (net.PacketConn, net.Addr, error) {
|
||||
return nil, nil, errors.New("ws client does not support udp now")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user