glider/proxy/pxyproto/server.go
2022-01-08 15:05:55 +08:00

135 lines
2.8 KiB
Go

package pxyproto
import (
"errors"
"fmt"
"net"
"net/url"
"strings"
"github.com/nadoo/glider/pkg/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.Fatalf("[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 }