proxy: support server mode of PROXY protocol v1

This commit is contained in:
nadoo 2021-08-04 19:13:22 +08:00
parent 32e1c37cfe
commit 7486373821
15 changed files with 173 additions and 37 deletions

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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