forwarder: add the ability to get parameters like 'priority'

This commit is contained in:
nadoo 2018-08-12 12:37:25 +08:00
parent e3888a6bd3
commit a46ab20901
34 changed files with 1480 additions and 1710 deletions

14
conf.go
View File

@ -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")

View File

@ -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
}

View File

@ -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
View File

@ -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...)
}
}
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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 + "'")

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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)
}
}()
}
}

View File

@ -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)
}
}()
}
}

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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)

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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")
}

View File

@ -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")
}