mirror of
https://github.com/fatedier/frp.git
synced 2024-12-16 19:50:58 +01:00
change the Host value in http request header
This commit is contained in:
parent
4067591a4d
commit
e0f2993b70
@ -144,6 +144,8 @@ func loginToServer(cli *client.ProxyClient) (c *conn.Conn, err error) {
|
|||||||
UseGzip: cli.UseGzip,
|
UseGzip: cli.UseGzip,
|
||||||
PrivilegeMode: cli.PrivilegeMode,
|
PrivilegeMode: cli.PrivilegeMode,
|
||||||
ProxyType: cli.Type,
|
ProxyType: cli.Type,
|
||||||
|
LocalIp: cli.LocalIp,
|
||||||
|
LocalPort: cli.LocalPort,
|
||||||
Timestamp: nowTime,
|
Timestamp: nowTime,
|
||||||
}
|
}
|
||||||
if cli.PrivilegeMode {
|
if cli.PrivilegeMode {
|
||||||
|
@ -276,6 +276,8 @@ func doLogin(req *msg.ControlReq, c *conn.Conn) (ret int64, info string) {
|
|||||||
// set infomations from frpc
|
// set infomations from frpc
|
||||||
s.UseEncryption = req.UseEncryption
|
s.UseEncryption = req.UseEncryption
|
||||||
s.UseGzip = req.UseGzip
|
s.UseGzip = req.UseGzip
|
||||||
|
s.ClientIp = req.LocalIp
|
||||||
|
s.ClientPort = req.LocalPort
|
||||||
|
|
||||||
// start proxy and listen for user connections, no block
|
// start proxy and listen for user connections, no block
|
||||||
err := s.Start(c)
|
err := s.Start(c)
|
||||||
|
@ -22,4 +22,7 @@ type BaseConf struct {
|
|||||||
UseGzip bool
|
UseGzip bool
|
||||||
PrivilegeMode bool
|
PrivilegeMode bool
|
||||||
PrivilegeToken string
|
PrivilegeToken string
|
||||||
|
ClientIp string
|
||||||
|
ClientPort int64
|
||||||
|
ServerPort int64
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ type ControlReq struct {
|
|||||||
AuthKey string `json:"auth_key"`
|
AuthKey string `json:"auth_key"`
|
||||||
UseEncryption bool `json:"use_encryption"`
|
UseEncryption bool `json:"use_encryption"`
|
||||||
UseGzip bool `json:"use_gzip"`
|
UseGzip bool `json:"use_gzip"`
|
||||||
|
LocalIp string `json:"local_ip"`
|
||||||
|
LocalPort int64 `json:"local_port"`
|
||||||
|
|
||||||
// configures used if privilege_mode is enabled
|
// configures used if privilege_mode is enabled
|
||||||
PrivilegeMode bool `json:"privilege_mode"`
|
PrivilegeMode bool `json:"privilege_mode"`
|
||||||
|
@ -64,6 +64,7 @@ func NewProxyServerFromCtlMsg(req *msg.ControlReq) (p *ProxyServer) {
|
|||||||
p.BindAddr = BindAddr
|
p.BindAddr = BindAddr
|
||||||
p.ListenPort = req.RemotePort
|
p.ListenPort = req.RemotePort
|
||||||
p.CustomDomains = req.CustomDomains
|
p.CustomDomains = req.CustomDomains
|
||||||
|
p.ServerPort = VhostHttpPort
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +114,7 @@ func (p *ProxyServer) Start(c *conn.Conn) (err error) {
|
|||||||
p.listeners = append(p.listeners, l)
|
p.listeners = append(p.listeners, l)
|
||||||
} else if p.Type == "http" {
|
} else if p.Type == "http" {
|
||||||
for _, domain := range p.CustomDomains {
|
for _, domain := range p.CustomDomains {
|
||||||
l, err := VhostHttpMuxer.Listen(domain)
|
l, err := VhostHttpMuxer.Listen(domain, p.Type, p.ClientIp, p.ClientPort, p.ServerPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -121,7 +122,7 @@ func (p *ProxyServer) Start(c *conn.Conn) (err error) {
|
|||||||
}
|
}
|
||||||
} else if p.Type == "https" {
|
} else if p.Type == "https" {
|
||||||
for _, domain := range p.CustomDomains {
|
for _, domain := range p.CustomDomains {
|
||||||
l, err := VhostHttpsMuxer.Listen(domain)
|
l, err := VhostHttpsMuxer.Listen(domain, p.Type, p.ClientIp, p.ClientPort, p.ServerPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,17 @@ package vhost
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"frp/utils/conn"
|
"frp/utils/conn"
|
||||||
|
"frp/utils/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HttpMuxer struct {
|
type HttpMuxer struct {
|
||||||
@ -45,3 +50,112 @@ func NewHttpMuxer(listener *conn.Listener, timeout time.Duration) (*HttpMuxer, e
|
|||||||
mux, err := NewVhostMuxer(listener, GetHttpHostname, timeout)
|
mux, err := NewVhostMuxer(listener, GetHttpHostname, timeout)
|
||||||
return &HttpMuxer{mux}, err
|
return &HttpMuxer{mux}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func HostNameRewrite(c *conn.Conn, clientHost string) (_ net.Conn, err error) {
|
||||||
|
log.Info("HostNameRewrite, clientHost: %s", clientHost)
|
||||||
|
sc, rd := newShareConn(c.TcpConn)
|
||||||
|
var buff []byte
|
||||||
|
if buff, err = hostNameRewrite(rd, clientHost); err != nil {
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
err = sc.WriteBuff(buff)
|
||||||
|
return sc, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func hostNameRewrite(request io.Reader, clientHost string) (_ []byte, err error) {
|
||||||
|
buffer := make([]byte, 1024)
|
||||||
|
request.Read(buffer)
|
||||||
|
log.Debug("before hostNameRewrite:\n %s", string(buffer))
|
||||||
|
retBuffer, err := parseRequest(buffer, clientHost)
|
||||||
|
log.Debug("after hostNameRewrite:\n %s", string(retBuffer))
|
||||||
|
return retBuffer, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRequest(org []byte, clientHost string) (ret []byte, err error) {
|
||||||
|
tp := bytes.NewBuffer(org)
|
||||||
|
// First line: GET /index.html HTTP/1.0
|
||||||
|
var b []byte
|
||||||
|
if b, err = tp.ReadBytes('\n'); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req := new(http.Request)
|
||||||
|
//we invoked ReadRequest in GetHttpHostname before, so we ignore error
|
||||||
|
req.Method, req.RequestURI, req.Proto, _ = parseRequestLine(string(b))
|
||||||
|
rawurl := req.RequestURI
|
||||||
|
//CONNECT www.google.com:443 HTTP/1.1
|
||||||
|
justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/")
|
||||||
|
if justAuthority {
|
||||||
|
rawurl = "http://" + rawurl
|
||||||
|
}
|
||||||
|
req.URL, _ = url.ParseRequestURI(rawurl)
|
||||||
|
if justAuthority {
|
||||||
|
// Strip the bogus "http://" back off.
|
||||||
|
req.URL.Scheme = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC2616: first case
|
||||||
|
// GET /index.html HTTP/1.1
|
||||||
|
// Host: www.google.com
|
||||||
|
if req.URL.Host == "" {
|
||||||
|
changedBuf, err := changeHostName(tp, clientHost)
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.Write(b)
|
||||||
|
buf.Write(changedBuf)
|
||||||
|
return buf.Bytes(), err
|
||||||
|
}
|
||||||
|
|
||||||
|
// RFC2616: second case
|
||||||
|
// GET http://www.google.com/index.html HTTP/1.1
|
||||||
|
// Host: doesntmatter
|
||||||
|
// In this case, any Host line is ignored.
|
||||||
|
req.URL.Host = clientHost
|
||||||
|
firstLine := req.Method + " " + req.URL.String() + " " + req.Proto
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.WriteString(firstLine)
|
||||||
|
tp.WriteTo(buf)
|
||||||
|
return buf.Bytes(), err
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseRequestLine parses "GET /foo HTTP/1.1" into its three parts.
|
||||||
|
func parseRequestLine(line string) (method, requestURI, proto string, ok bool) {
|
||||||
|
s1 := strings.Index(line, " ")
|
||||||
|
s2 := strings.Index(line[s1+1:], " ")
|
||||||
|
if s1 < 0 || s2 < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s2 += s1 + 1
|
||||||
|
return line[:s1], line[s1+1 : s2], line[s2+1:], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func changeHostName(buff *bytes.Buffer, clientHost string) (_ []byte, err error) {
|
||||||
|
retBuf := new(bytes.Buffer)
|
||||||
|
|
||||||
|
peek := buff.Bytes()
|
||||||
|
for len(peek) > 0 {
|
||||||
|
i := bytes.IndexByte(peek, '\n')
|
||||||
|
if i < 3 {
|
||||||
|
// Not present (-1) or found within the next few bytes,
|
||||||
|
// implying we're at the end ("\r\n\r\n" or "\n\n")
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
kv := peek[:i]
|
||||||
|
j := bytes.IndexByte(kv, ':')
|
||||||
|
if j < 0 {
|
||||||
|
return nil, fmt.Errorf("malformed MIME header line: " + string(kv))
|
||||||
|
}
|
||||||
|
if strings.Contains(strings.ToLower(string(kv[:j])), "host") {
|
||||||
|
hostHeader := fmt.Sprintf("Host: %s\n", clientHost)
|
||||||
|
retBuf.WriteString(hostHeader)
|
||||||
|
peek = peek[i+1:]
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
retBuf.Write(peek[:i])
|
||||||
|
retBuf.WriteByte('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
peek = peek[i+1:]
|
||||||
|
}
|
||||||
|
retBuf.Write(peek)
|
||||||
|
return retBuf.Bytes(), err
|
||||||
|
}
|
||||||
|
@ -34,6 +34,10 @@ type VhostMuxer struct {
|
|||||||
vhostFunc muxFunc
|
vhostFunc muxFunc
|
||||||
registryMap map[string]*Listener
|
registryMap map[string]*Listener
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
|
|
||||||
|
//build map between custom_domains and client_domain
|
||||||
|
domainMap map[string]string
|
||||||
|
domainMutex sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewVhostMuxer(listener *conn.Listener, vhostFunc muxFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
|
func NewVhostMuxer(listener *conn.Listener, vhostFunc muxFunc, timeout time.Duration) (mux *VhostMuxer, err error) {
|
||||||
@ -47,7 +51,7 @@ func NewVhostMuxer(listener *conn.Listener, vhostFunc muxFunc, timeout time.Dura
|
|||||||
return mux, nil
|
return mux, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *VhostMuxer) Listen(name string) (l *Listener, err error) {
|
func (v *VhostMuxer) Listen(name, proxytype, clientIp string, clientPort, serverPort int64) (l *Listener, err error) {
|
||||||
v.mutex.Lock()
|
v.mutex.Lock()
|
||||||
defer v.mutex.Unlock()
|
defer v.mutex.Unlock()
|
||||||
if _, exist := v.registryMap[name]; exist {
|
if _, exist := v.registryMap[name]; exist {
|
||||||
@ -58,6 +62,10 @@ func (v *VhostMuxer) Listen(name string) (l *Listener, err error) {
|
|||||||
name: name,
|
name: name,
|
||||||
mux: v,
|
mux: v,
|
||||||
accept: make(chan *conn.Conn),
|
accept: make(chan *conn.Conn),
|
||||||
|
proxyType: proxytype,
|
||||||
|
clientIp: clientIp,
|
||||||
|
clientPort: clientPort,
|
||||||
|
serverPort: serverPort,
|
||||||
}
|
}
|
||||||
v.registryMap[name] = l
|
v.registryMap[name] = l
|
||||||
return l, nil
|
return l, nil
|
||||||
@ -114,6 +122,10 @@ type Listener struct {
|
|||||||
name string
|
name string
|
||||||
mux *VhostMuxer // for closing VhostMuxer
|
mux *VhostMuxer // for closing VhostMuxer
|
||||||
accept chan *conn.Conn
|
accept chan *conn.Conn
|
||||||
|
proxyType string //suppor http host rewrite
|
||||||
|
clientIp string
|
||||||
|
clientPort int64
|
||||||
|
serverPort int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Listener) Accept() (*conn.Conn, error) {
|
func (l *Listener) Accept() (*conn.Conn, error) {
|
||||||
@ -121,6 +133,20 @@ func (l *Listener) Accept() (*conn.Conn, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("Listener closed")
|
return nil, fmt.Errorf("Listener closed")
|
||||||
}
|
}
|
||||||
|
if net.ParseIP(l.clientIp) == nil && l.proxyType == "http" {
|
||||||
|
if (l.name != l.clientIp) || (l.serverPort != l.clientPort) {
|
||||||
|
clientHost := l.clientIp
|
||||||
|
if l.clientPort != 80 {
|
||||||
|
strPort := fmt.Sprintf(":%d", l.clientPort)
|
||||||
|
clientHost += strPort
|
||||||
|
}
|
||||||
|
retConn, err := HostNameRewrite(conn, clientHost)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("http host rewrite failed")
|
||||||
|
}
|
||||||
|
conn.SetTcpConn(retConn)
|
||||||
|
}
|
||||||
|
}
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,3 +192,9 @@ func (sc *sharedConn) Read(p []byte) (n int, err error) {
|
|||||||
sc.Unlock()
|
sc.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sc *sharedConn) WriteBuff(buffer []byte) (err error) {
|
||||||
|
sc.buff.Reset()
|
||||||
|
_, err = sc.buff.Write(buffer)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user