|
|
@ -1,11 +1,16 @@
|
|
|
|
package proxy
|
|
|
|
package proxy
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
import (
|
|
|
|
|
|
|
|
"bufio"
|
|
|
|
"context"
|
|
|
|
"context"
|
|
|
|
"crypto/tls"
|
|
|
|
"crypto/tls"
|
|
|
|
"encoding/json"
|
|
|
|
"encoding/json"
|
|
|
|
|
|
|
|
"errors"
|
|
|
|
"net"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
"net/url"
|
|
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
uuid "github.com/satori/go.uuid"
|
|
|
|
uuid "github.com/satori/go.uuid"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
@ -138,13 +143,24 @@ func (connCtx *ConnContext) initHttpServerConn() {
|
|
|
|
connCtx.ServerConn = serverConn
|
|
|
|
connCtx.ServerConn = serverConn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (connCtx *ConnContext) initServerTcpConn() error {
|
|
|
|
func (connCtx *ConnContext) initServerTcpConn(req *http.Request) error {
|
|
|
|
log.Debugln("in initServerTcpConn")
|
|
|
|
log.Debugln("in initServerTcpConn")
|
|
|
|
ServerConn := newServerConn()
|
|
|
|
ServerConn := newServerConn()
|
|
|
|
connCtx.ServerConn = ServerConn
|
|
|
|
connCtx.ServerConn = ServerConn
|
|
|
|
ServerConn.Address = connCtx.pipeConn.host
|
|
|
|
ServerConn.Address = connCtx.pipeConn.host
|
|
|
|
|
|
|
|
|
|
|
|
plainConn, err := (&net.Dialer{}).DialContext(context.Background(), "tcp", ServerConn.Address)
|
|
|
|
// test is use proxy
|
|
|
|
|
|
|
|
clientReq := &http.Request{URL: &url.URL{Scheme: "https", Host: ServerConn.Address}}
|
|
|
|
|
|
|
|
proxyUrl, err := http.ProxyFromEnvironment(clientReq)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
var plainConn net.Conn
|
|
|
|
|
|
|
|
if proxyUrl != nil {
|
|
|
|
|
|
|
|
plainConn, err = getProxyConn(proxyUrl, ServerConn.Address)
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
plainConn, err = (&net.Dialer{}).DialContext(context.Background(), "tcp", ServerConn.Address)
|
|
|
|
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -161,13 +177,63 @@ func (connCtx *ConnContext) initServerTcpConn() error {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// connect proxy when set https_proxy env
|
|
|
|
|
|
|
|
// ref: http/transport.go dialConn func
|
|
|
|
|
|
|
|
func getProxyConn(proxyUrl *url.URL, address string) (net.Conn, error) {
|
|
|
|
|
|
|
|
conn, err := (&net.Dialer{}).DialContext(context.Background(), "tcp", proxyUrl.Host)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
connectReq := &http.Request{
|
|
|
|
|
|
|
|
Method: "CONNECT",
|
|
|
|
|
|
|
|
URL: &url.URL{Opaque: address},
|
|
|
|
|
|
|
|
Host: address,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
connectCtx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
|
|
|
|
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
didReadResponse := make(chan struct{}) // closed after CONNECT write+read is done or fails
|
|
|
|
|
|
|
|
var resp *http.Response
|
|
|
|
|
|
|
|
// Write the CONNECT request & read the response.
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
|
|
defer close(didReadResponse)
|
|
|
|
|
|
|
|
err = connectReq.Write(conn)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Okay to use and discard buffered reader here, because
|
|
|
|
|
|
|
|
// TLS server will not speak until spoken to.
|
|
|
|
|
|
|
|
br := bufio.NewReader(conn)
|
|
|
|
|
|
|
|
resp, err = http.ReadResponse(br, connectReq)
|
|
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
|
|
|
|
case <-connectCtx.Done():
|
|
|
|
|
|
|
|
conn.Close()
|
|
|
|
|
|
|
|
<-didReadResponse
|
|
|
|
|
|
|
|
return nil, connectCtx.Err()
|
|
|
|
|
|
|
|
case <-didReadResponse:
|
|
|
|
|
|
|
|
// resp or err now set
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
|
|
conn.Close()
|
|
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
|
|
|
|
_, text, ok := strings.Cut(resp.Status, " ")
|
|
|
|
|
|
|
|
conn.Close()
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
|
|
|
|
return nil, errors.New("unknown status code")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errors.New(text)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return conn, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (connCtx *ConnContext) initHttpsServerConn() {
|
|
|
|
func (connCtx *ConnContext) initHttpsServerConn() {
|
|
|
|
if !connCtx.ClientConn.Tls {
|
|
|
|
if !connCtx.ClientConn.Tls {
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
connCtx.ServerConn.client = &http.Client{
|
|
|
|
connCtx.ServerConn.client = &http.Client{
|
|
|
|
Transport: &http.Transport{
|
|
|
|
Transport: &http.Transport{
|
|
|
|
Proxy: http.ProxyFromEnvironment,
|
|
|
|
|
|
|
|
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
|
|
|
<-connCtx.ServerConn.tlsHandshaked
|
|
|
|
<-connCtx.ServerConn.tlsHandshaked
|
|
|
|
return connCtx.ServerConn.tlsConn, connCtx.ServerConn.tlsHandshakeErr
|
|
|
|
return connCtx.ServerConn.tlsConn, connCtx.ServerConn.tlsHandshakeErr
|
|
|
|