mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 01:15:41 +08:00
proxy: support server mode of PROXY protocol v1
This commit is contained in:
parent
32e1c37cfe
commit
7486373821
@ -65,7 +65,8 @@ we can set up local listeners as proxy servers, and forward requests to internet
|
||||
|Unix |√|√|√|√|transport client & server
|
||||
|Smux |√| |√| |transport client & server
|
||||
|Websocket(WS)|√| |√| |transport client & server
|
||||
|WS Secure |√| |√| |transport client & server
|
||||
|WS Secure |√| |√| |websocket secure (wss)
|
||||
|Proxy Proto |√| |√| |transport client & server
|
||||
|Simple-Obfs | | |√| |transport client only
|
||||
|Redir |√| | | |linux redirect proxy
|
||||
|Redir6 |√| | | |linux redirect proxy(ipv6)
|
||||
@ -182,8 +183,8 @@ glider -verbose -listen :8443 -forward SCHEME://HOST:PORT
|
||||
|
||||
```bash
|
||||
Available schemes:
|
||||
listen: mixed ss socks5 http vless trojan trojanc redir redir6 tproxy tcp udp tls ws wss unix smux kcp
|
||||
forward: direct reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws wss unix smux kcp simple-obfs
|
||||
listen: mixed ss socks5 http vless trojan trojanc redir redir6 tproxy tcp udp tls ws wss unix smux kcp pxyproto
|
||||
forward: direct reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws wss unix smux kcp simple-obfs pxyproto
|
||||
|
||||
Socks5 scheme:
|
||||
socks://[user:pass@]host:port
|
||||
@ -397,7 +398,7 @@ Examples:
|
||||
// _ "github.com/nadoo/glider/proxy/kcp"
|
||||
```
|
||||
|
||||
3. Build it(requires **Go 1.16+** )
|
||||
3. Build it(requires **Go 1.17+** )
|
||||
```bash
|
||||
go build -v -ldflags "-s -w"
|
||||
```
|
||||
|
@ -150,8 +150,8 @@ func usage() {
|
||||
fmt.Fprintf(w, "\n")
|
||||
|
||||
fmt.Fprintf(w, "Available schemes:\n")
|
||||
fmt.Fprintf(w, " listen: mixed ss socks5 http vless trojan trojanc redir redir6 tproxy tcp udp tls ws wss unix smux kcp\n")
|
||||
fmt.Fprintf(w, " forward: direct reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws wss unix smux kcp simple-obfs\n")
|
||||
fmt.Fprintf(w, " listen: mixed ss socks5 http vless trojan trojanc redir redir6 tproxy tcp udp tls ws wss unix smux kcp pxyproto\n")
|
||||
fmt.Fprintf(w, " forward: direct reject ss socks4 socks5 http ssr ssh vless vmess trojan trojanc tcp udp tls ws wss unix smux kcp simple-obfs pxyproto\n")
|
||||
fmt.Fprintf(w, "\n")
|
||||
|
||||
fmt.Fprintf(w, "Socks5 scheme:\n")
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
// UDPMaxLen is the max size of udp dns request.
|
||||
// https://tools.ietf.org/html/rfc1035#section-4.2.1
|
||||
// https://www.rfc-editor.org/rfc/rfc1035#section-4.2.1
|
||||
// Messages carried by UDP are restricted to 512 bytes (not counting the IP
|
||||
// or UDP headers). Longer messages are truncated and the TC bit is set in
|
||||
// the header.
|
||||
@ -36,7 +36,7 @@ const (
|
||||
const ClassINET uint16 = 1
|
||||
|
||||
// Message format:
|
||||
// https://tools.ietf.org/html/rfc1035#section-4.1
|
||||
// https://www.rfc-editor.org/rfc/rfc1035#section-4.1
|
||||
// All communications inside of the domain protocol are carried in a single
|
||||
// format called a message. The top level format of message is divided
|
||||
// into 5 sections (some of which are empty in certain cases) shown below:
|
||||
@ -165,7 +165,7 @@ func UnmarshalMessage(b []byte) (*Message, error) {
|
||||
}
|
||||
|
||||
// Header format:
|
||||
// https://tools.ietf.org/html/rfc1035#section-4.1.1
|
||||
// https://www.rfc-editor.org/rfc/rfc1035#section-4.1.1
|
||||
// The header contains the following fields:
|
||||
//
|
||||
// 1 1 1 1 1 1
|
||||
@ -244,7 +244,7 @@ func UnmarshalHeader(b []byte, h *Header) error {
|
||||
}
|
||||
|
||||
// Question format:
|
||||
// https://tools.ietf.org/html/rfc1035#section-4.1.2
|
||||
// https://www.rfc-editor.org/rfc/rfc1035#section-4.1.2
|
||||
// The question section is used to carry the "question" in most queries,
|
||||
// i.e., the parameters that define what is being asked. The section
|
||||
// contains QDCOUNT (usually 1) entries, each of the following format:
|
||||
@ -322,8 +322,8 @@ func (m *Message) UnmarshalQuestion(b []byte, q *Question) (n int, err error) {
|
||||
}
|
||||
|
||||
// RR format:
|
||||
// https://tools.ietf.org/html/rfc1035#section-3.2.1
|
||||
// https://tools.ietf.org/html/rfc1035#section-4.1.3
|
||||
// https://www.rfc-editor.org/rfc/rfc1035#section-3.2.1
|
||||
// https://www.rfc-editor.org/rfc/rfc1035#section-4.1.3
|
||||
// The answer, authority, and additional sections all share the same
|
||||
// format: a variable number of resource records, where the number of
|
||||
// records is specified in the corresponding count field in the header.
|
||||
@ -479,7 +479,7 @@ func (m *Message) UnmarshalDomainTo(sb *strings.Builder, b []byte) (int, error)
|
||||
var idx, size int
|
||||
|
||||
for len(b[idx:]) != 0 {
|
||||
// https://tools.ietf.org/html/rfc1035#section-4.1.4
|
||||
// https://www.rfc-editor.org/rfc/rfc1035#section-4.1.4
|
||||
// "Message compression",
|
||||
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
||||
// | 1 1| OFFSET |
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
_ "github.com/nadoo/glider/proxy/kcp"
|
||||
_ "github.com/nadoo/glider/proxy/mixed"
|
||||
_ "github.com/nadoo/glider/proxy/obfs"
|
||||
_ "github.com/nadoo/glider/proxy/pxyproto"
|
||||
_ "github.com/nadoo/glider/proxy/reject"
|
||||
_ "github.com/nadoo/glider/proxy/smux"
|
||||
_ "github.com/nadoo/glider/proxy/socks4"
|
||||
|
4
go.mod
4
go.mod
@ -9,8 +9,8 @@ require (
|
||||
github.com/dgryski/go-rc2 v0.0.0-20150621095337-8a9021637152
|
||||
github.com/ebfe/rc2 v0.0.0-20131011165748-24b9757f5521 // indirect
|
||||
github.com/insomniacslk/dhcp v0.0.0-20210621130208-1cac67f12b1e
|
||||
github.com/klauspost/cpuid/v2 v2.0.8 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.12 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.13 // indirect
|
||||
github.com/mdlayher/raw v0.0.0-20210412142147-51b895745faf // indirect
|
||||
github.com/nadoo/conflag v0.2.3
|
||||
github.com/nadoo/ipset v0.3.0
|
||||
|
10
go.sum
10
go.sum
@ -49,12 +49,12 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
|
||||
github.com/klauspost/cpuid v1.2.4/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
|
||||
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.2/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.8 h1:bhR2mgIlno/Sfk4oUbH4sPlc83z1yGrN9bvqiq3C33I=
|
||||
github.com/klauspost/cpuid/v2 v2.0.8/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/reedsolomon v1.9.9/go.mod h1:O7yFFHiQwDR6b2t63KPUpccPtNdp5ADgh1gg4fd12wo=
|
||||
github.com/klauspost/reedsolomon v1.9.12 h1:EyOucRmcrLH+2hqKGdoA5SM8pwPKR6BJsf3r6zpYOA0=
|
||||
github.com/klauspost/reedsolomon v1.9.12/go.mod h1:nLvuzNvy1ZDNQW30IuMc2ZWCbiqrJgdLoUS2X8HAUVg=
|
||||
github.com/klauspost/reedsolomon v1.9.13 h1:Xr0COKf7F0ACTXUNnz2ZFCWlUKlUTAUX3y7BODdUxqU=
|
||||
github.com/klauspost/reedsolomon v1.9.13/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk=
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 h1:lez6TS6aAau+8wXUP3G9I3TGlmPFEq2CTxBaRqY6AGE=
|
||||
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
|
@ -45,6 +45,12 @@ func (c *Conn) Peek(n int) ([]byte, error) { return c.r.Peek(n) }
|
||||
// WriteTo implements io.WriterTo.
|
||||
func (c *Conn) WriteTo(w io.Writer) (n int64, err error) { return c.r.WriteTo(w) }
|
||||
|
||||
// Close closes the Conn.
|
||||
func (c *Conn) Close() error {
|
||||
pool.PutBufReader(c.r)
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
// Relay relays between left and right.
|
||||
func Relay(left, right net.Conn) error {
|
||||
var err, err1 error
|
||||
@ -73,12 +79,6 @@ func Relay(left, right net.Conn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close closes the Conn.
|
||||
func (c *Conn) Close() error {
|
||||
pool.PutBufReader(c.r)
|
||||
return c.Conn.Close()
|
||||
}
|
||||
|
||||
// Copy copies from src to dst.
|
||||
func Copy(dst io.Writer, src io.Reader) (written int64, err error) {
|
||||
dst = underlyingWriter(dst)
|
||||
|
@ -3,7 +3,7 @@ package http
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"strings"
|
||||
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// Methods are http methods from rfc.
|
||||
// https://www.ietf.org/rfc/rfc2616.txt, http methods must be uppercase
|
||||
// https://www.rfc-editor.org/rfc/rfc2616, http methods must be uppercase
|
||||
var Methods = [...][]byte{
|
||||
[]byte("GET"),
|
||||
[]byte("POST"),
|
||||
@ -46,7 +46,7 @@ func parseRequest(r *bufio.Reader) (*request, error) {
|
||||
|
||||
method, uri, proto, ok := parseStartLine(line)
|
||||
if !ok {
|
||||
return nil, errors.New("error in parseStartLine")
|
||||
return nil, fmt.Errorf("error in parseStartLine: %s", line)
|
||||
}
|
||||
|
||||
header, err := tpr.ReadMIMEHeader()
|
||||
|
@ -51,7 +51,7 @@ func (s *HTTP) Serve(cc net.Conn) {
|
||||
c := proxy.NewConn(cc)
|
||||
req, err := parseRequest(c.Reader())
|
||||
if err != nil {
|
||||
log.F("[http] can not parse request from %s", c.RemoteAddr())
|
||||
log.F("[http] can not parse request from %s, error: %v", c.RemoteAddr(), err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
// https://www.ietf.org/rfc/rfc5246.txt
|
||||
// https://www.rfc-editor.org/rfc/rfc5246
|
||||
// https://golang.org/src/crypto/tls/handshake_messages.go
|
||||
|
||||
// NOTE:
|
||||
|
134
proxy/pxyproto/server.go
Normal file
134
proxy/pxyproto/server.go
Normal file
@ -0,0 +1,134 @@
|
||||
package pxyproto
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/nadoo/glider/log"
|
||||
"github.com/nadoo/glider/proxy"
|
||||
)
|
||||
|
||||
func init() {
|
||||
proxy.RegisterServer("pxyproto", NewPxyProtoServer)
|
||||
}
|
||||
|
||||
// PxyProtoServer struct.
|
||||
type PxyProtoServer struct {
|
||||
addr string
|
||||
proxy proxy.Proxy
|
||||
server proxy.Server
|
||||
}
|
||||
|
||||
// NewPxyProtoServer returns a PxyProtoServer struct.
|
||||
func NewPxyProtoServer(s string, p proxy.Proxy) (proxy.Server, error) {
|
||||
schemes := strings.SplitN(s, ",", 2)
|
||||
u, err := url.Parse(schemes[0])
|
||||
if err != nil {
|
||||
log.F("[pxyproto] parse url err: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t := &PxyProtoServer{proxy: p, addr: u.Host}
|
||||
if len(schemes) < 2 {
|
||||
return nil, errors.New("[pxyproto] you must use pxyproto with a proxy server, e.g: pxyproto://:1234,http://")
|
||||
}
|
||||
|
||||
t.server, err = proxy.ServerFromURL(schemes[1], p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// ListenAndServe listens on server's addr and serves connections.
|
||||
func (s *PxyProtoServer) ListenAndServe() {
|
||||
l, err := net.Listen("tcp", s.addr)
|
||||
if err != nil {
|
||||
log.F("[pxyproto] failed to listen on %s: %v", s.addr, err)
|
||||
return
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
log.F("[pxyproto] listening TCP on %s", s.addr)
|
||||
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
log.F("[pxyproto] failed to accept: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
go s.Serve(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Serve serves a connection.
|
||||
func (s *PxyProtoServer) Serve(cc net.Conn) {
|
||||
c, err := newServerConn(cc)
|
||||
if err != nil {
|
||||
log.F("[pxyproto] parse header failed, error: %v", err)
|
||||
cc.Close()
|
||||
return
|
||||
}
|
||||
|
||||
// log.F("[pxyproto] %s <-> %s <-> %s <-> %s",
|
||||
// c.RemoteAddr(), c.LocalAddr(), cc.RemoteAddr(), cc.LocalAddr())
|
||||
|
||||
if s.server != nil {
|
||||
s.server.Serve(c)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
type serverConn struct {
|
||||
*proxy.Conn
|
||||
src, dst net.Addr
|
||||
}
|
||||
|
||||
func newServerConn(c net.Conn) (*serverConn, error) {
|
||||
sc := &serverConn{
|
||||
Conn: proxy.NewConn(c),
|
||||
src: c.RemoteAddr(),
|
||||
dst: c.LocalAddr(),
|
||||
}
|
||||
return sc, sc.parseHeader()
|
||||
}
|
||||
|
||||
// "PROXY TCPx SRC_IP DST_IP SRC_PORT DST_PORT"
|
||||
func (c *serverConn) parseHeader() error {
|
||||
line, err := c.Conn.Reader().ReadString('\n')
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
line = strings.ReplaceAll(line, "\r\n", "")
|
||||
// log.F("[pxyproto] req header: %s", line)
|
||||
|
||||
header := strings.Split(line, " ")
|
||||
if len(header) != 6 {
|
||||
return fmt.Errorf("invalid header: %s", line)
|
||||
}
|
||||
|
||||
if header[0] != "PROXY" {
|
||||
return fmt.Errorf("invalid header: %s", line)
|
||||
}
|
||||
|
||||
c.src, err = net.ResolveTCPAddr("tcp", net.JoinHostPort(header[2], header[4]))
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse header: %s, error: %v", line, err)
|
||||
}
|
||||
|
||||
c.dst, err = net.ResolveTCPAddr("tcp", net.JoinHostPort(header[3], header[5]))
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse header: %s, error: %v", line, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *serverConn) LocalAddr() net.Addr { return c.dst }
|
||||
func (c *serverConn) RemoteAddr() net.Addr { return c.src }
|
@ -66,7 +66,7 @@ func (pc *PktConn) ReadFrom(b []byte) (int, net.Addr, error) {
|
||||
return n, raddr, errors.New("not enough size to get addr")
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc1928#section-7
|
||||
// https://www.rfc-editor.org/rfc/rfc1928#section-7
|
||||
// +----+------+------+----------+----------+----------+
|
||||
// |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
|
||||
// +----+------+------+----------+----------+----------+
|
||||
|
@ -1,4 +1,4 @@
|
||||
// https://tools.ietf.org/html/rfc1928
|
||||
// https://www.rfc-editor.org/rfc/rfc1928
|
||||
|
||||
// socks5 client:
|
||||
// https://github.com/golang/net/tree/master/proxy
|
||||
|
@ -1,4 +1,4 @@
|
||||
// https://tools.ietf.org/html/rfc6455#section-5.2
|
||||
// https://www.rfc-editor.org/rfc/rfc6455#section-5.2
|
||||
//
|
||||
// Frame Format
|
||||
// 0 1 2 3
|
||||
|
@ -1,12 +1,12 @@
|
||||
package rule
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/nadoo/glider/pool"
|
||||
@ -71,12 +71,12 @@ func (c *httpChecker) Check(dialer proxy.Dialer) (time.Duration, error) {
|
||||
r := pool.GetBufReader(rc)
|
||||
defer pool.PutBufReader(r)
|
||||
|
||||
line, _, err := r.ReadLine()
|
||||
line, err := r.ReadString('\n')
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if !bytes.Contains(line, []byte(c.expect)) {
|
||||
if !strings.Contains(line, c.expect) {
|
||||
return 0, fmt.Errorf("expect: %s, got: %s", c.expect, line)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user