mirror of
https://github.com/gospider007/requests.git
synced 2025-12-24 13:57:52 +08:00
sync
This commit is contained in:
356
dial.go
356
dial.go
@@ -8,7 +8,6 @@ import (
|
||||
"io"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -22,7 +21,7 @@ import (
|
||||
|
||||
type msgClient struct {
|
||||
time time.Time
|
||||
host string
|
||||
ip net.IP
|
||||
}
|
||||
|
||||
type DialOption struct {
|
||||
@@ -32,7 +31,6 @@ type DialOption struct {
|
||||
AddrType gtls.AddrType //first ip type
|
||||
Dns *net.UDPAddr
|
||||
GetAddrType func(host string) gtls.AddrType
|
||||
// EnableNetPoll bool
|
||||
}
|
||||
|
||||
type dialer interface {
|
||||
@@ -55,6 +53,7 @@ func (d *myDialer) DialContext(ctx context.Context, network string, address stri
|
||||
func (d *myDialer) LookupIPAddr(ctx context.Context, host string) ([]net.IPAddr, error) {
|
||||
return d.dialer.Resolver.LookupIPAddr(ctx, host)
|
||||
}
|
||||
|
||||
func newDialer(option DialOption) dialer {
|
||||
if option.KeepAlive == 0 {
|
||||
option.KeepAlive = time.Second * 5
|
||||
@@ -92,52 +91,35 @@ func newDialer(option DialOption) dialer {
|
||||
dialer.dialer.SetMultipathTCP(true)
|
||||
return &dialer
|
||||
}
|
||||
func (obj *Dialer) dialContext(ctx context.Context, option *RequestOption, network string, addr string, isProxy bool) (net.Conn, error) {
|
||||
func (obj *Dialer) dialContext(ctx context.Context, option *RequestOption, network string, addr Address, isProxy bool) (net.Conn, error) {
|
||||
if option == nil {
|
||||
option = &RequestOption{}
|
||||
}
|
||||
host, port, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, tools.WrapError(err, "addrToIp error,SplitHostPort")
|
||||
var err error
|
||||
if addr.IP == nil {
|
||||
addr.IP, err = obj.loadHost(ctx, addr.Name, option)
|
||||
}
|
||||
dialer := newDialer(option.DialOption)
|
||||
if _, ipInt := gtls.ParseHost(host); ipInt == 0 { //domain
|
||||
host, ok := obj.loadHost(host)
|
||||
if !ok { //dns parse
|
||||
var addrType gtls.AddrType
|
||||
if option.DialOption.AddrType != 0 {
|
||||
addrType = option.DialOption.AddrType
|
||||
} else if option.DialOption.GetAddrType != nil {
|
||||
addrType = option.DialOption.GetAddrType(host)
|
||||
}
|
||||
ips, err := dialer.LookupIPAddr(ctx, host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if host, err = obj.addrToIp(host, ips, addrType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if option.Logger != nil {
|
||||
if isProxy {
|
||||
option.Logger(Log{
|
||||
Id: option.requestId,
|
||||
Time: time.Now(),
|
||||
Type: LogType_ProxyDNSLookup,
|
||||
Msg: host,
|
||||
})
|
||||
} else {
|
||||
option.Logger(Log{
|
||||
Id: option.requestId,
|
||||
Time: time.Now(),
|
||||
Type: LogType_DNSLookup,
|
||||
Msg: host,
|
||||
})
|
||||
}
|
||||
}
|
||||
addr = net.JoinHostPort(host, port)
|
||||
if option.Logger != nil {
|
||||
if isProxy {
|
||||
option.Logger(Log{
|
||||
Id: option.requestId,
|
||||
Time: time.Now(),
|
||||
Type: LogType_ProxyDNSLookup,
|
||||
Msg: addr.Name,
|
||||
})
|
||||
} else {
|
||||
option.Logger(Log{
|
||||
Id: option.requestId,
|
||||
Time: time.Now(),
|
||||
Type: LogType_DNSLookup,
|
||||
Msg: addr.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
con, err := dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
con, err := newDialer(option.DialOption).DialContext(ctx, network, addr.String())
|
||||
if option.Logger != nil {
|
||||
if isProxy {
|
||||
option.Logger(Log{
|
||||
@@ -157,10 +139,10 @@ func (obj *Dialer) dialContext(ctx context.Context, option *RequestOption, netwo
|
||||
}
|
||||
return con, err
|
||||
}
|
||||
func (obj *Dialer) DialContext(ctx context.Context, ctxData *RequestOption, network string, addr string) (net.Conn, error) {
|
||||
func (obj *Dialer) DialContext(ctx context.Context, ctxData *RequestOption, network string, addr Address) (net.Conn, error) {
|
||||
return obj.dialContext(ctx, ctxData, network, addr, false)
|
||||
}
|
||||
func (obj *Dialer) ProxyDialContext(ctx context.Context, ctxData *RequestOption, network string, addr string) (net.Conn, error) {
|
||||
func (obj *Dialer) ProxyDialContext(ctx context.Context, ctxData *RequestOption, network string, addr Address) (net.Conn, error) {
|
||||
return obj.dialContext(ctx, ctxData, network, addr, true)
|
||||
}
|
||||
|
||||
@@ -208,7 +190,7 @@ func (obj *Dialer) dialProxyContext(ctx context.Context, ctxData *RequestOption,
|
||||
if ctxData == nil {
|
||||
ctxData = &RequestOption{}
|
||||
}
|
||||
addr, err := getProxyAddr(proxyUrl)
|
||||
addr, err := GetAddressWithUrl(proxyUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -244,7 +226,15 @@ func (obj *Dialer) verifyProxyToRemote(ctx context.Context, option *RequestOptio
|
||||
})
|
||||
}
|
||||
case "socks5":
|
||||
err = obj.clientVerifySocks5(conn, proxyUrl, remoteUrl)
|
||||
var proxyAddress Address
|
||||
var remoteAddress Address
|
||||
if proxyAddress, err = GetAddressWithUrl(proxyUrl); err != nil {
|
||||
return
|
||||
}
|
||||
if remoteAddress, err = GetAddressWithUrl(remoteUrl); err != nil {
|
||||
return
|
||||
}
|
||||
err = obj.verifyTCPSocks5(conn, proxyAddress, remoteAddress)
|
||||
if option.Logger != nil {
|
||||
option.Logger(Log{
|
||||
Id: option.requestId,
|
||||
@@ -263,118 +253,56 @@ func (obj *Dialer) verifyProxyToRemote(ctx context.Context, option *RequestOptio
|
||||
return conn, err
|
||||
}
|
||||
}
|
||||
func (obj *Dialer) loadHost(host string) (string, bool) {
|
||||
func (obj *Dialer) loadHost(ctx context.Context, host string, option *RequestOption) (net.IP, error) {
|
||||
msgDataAny, ok := obj.dnsIpData.Load(host)
|
||||
if ok {
|
||||
msgdata := msgDataAny.(msgClient)
|
||||
if time.Since(msgdata.time) < time.Second*60*5 {
|
||||
return msgdata.host, true
|
||||
return msgdata.ip, nil
|
||||
}
|
||||
}
|
||||
return host, false
|
||||
ip, ipInt := gtls.ParseHost(host)
|
||||
if ipInt != 0 {
|
||||
return ip, nil
|
||||
}
|
||||
var addrType gtls.AddrType
|
||||
if option.DialOption.AddrType != 0 {
|
||||
addrType = option.DialOption.AddrType
|
||||
} else if option.DialOption.GetAddrType != nil {
|
||||
addrType = option.DialOption.GetAddrType(host)
|
||||
}
|
||||
ips, err := newDialer(option.DialOption).LookupIPAddr(ctx, host)
|
||||
if err != nil {
|
||||
return net.IP{}, err
|
||||
}
|
||||
if ip, err = obj.addrToIp(host, ips, addrType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ip, nil
|
||||
}
|
||||
func (obj *Dialer) addrToIp(host string, ips []net.IPAddr, addrType gtls.AddrType) (string, error) {
|
||||
func (obj *Dialer) addrToIp(host string, ips []net.IPAddr, addrType gtls.AddrType) (net.IP, error) {
|
||||
ip, err := obj.lookupIPAddr(ips, addrType)
|
||||
if err != nil {
|
||||
return host, tools.WrapError(err, "addrToIp error,lookupIPAddr")
|
||||
return ip, tools.WrapError(err, "addrToIp error,lookupIPAddr")
|
||||
}
|
||||
obj.dnsIpData.Store(host, msgClient{time: time.Now(), host: ip.String()})
|
||||
return ip.String(), nil
|
||||
obj.dnsIpData.Store(host, msgClient{time: time.Now(), ip: ip})
|
||||
return ip, nil
|
||||
}
|
||||
func (obj *Dialer) clientVerifySocks5(conn net.Conn, proxyUrl *url.URL, remoteUrl *url.URL) (err error) {
|
||||
if _, err = conn.Write([]byte{5, 2, 0, 2}); err != nil {
|
||||
func (obj *Dialer) verifySocks5(conn net.Conn, network string, proxyAddr Address, remoteAddr Address) (proxyAddress Address, err error) {
|
||||
err = obj.verifySocks5Auth(conn, proxyAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
readCon := make([]byte, 4)
|
||||
if _, err = io.ReadFull(conn, readCon[:2]); err != nil {
|
||||
err = obj.writeCmd(conn, network)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
switch readCon[1] {
|
||||
case 2:
|
||||
if proxyUrl.User == nil {
|
||||
err = errors.New("socks5 need auth")
|
||||
return
|
||||
}
|
||||
pwd, pwdOk := proxyUrl.User.Password()
|
||||
if !pwdOk {
|
||||
err = errors.New("socks5 auth error")
|
||||
return
|
||||
}
|
||||
usr := proxyUrl.User.Username()
|
||||
|
||||
if usr == "" {
|
||||
err = errors.New("socks5 auth user format error")
|
||||
return
|
||||
}
|
||||
if _, err = conn.Write(append(
|
||||
append(
|
||||
[]byte{1, byte(len(usr))},
|
||||
tools.StringToBytes(usr)...,
|
||||
),
|
||||
append(
|
||||
[]byte{byte(len(pwd))},
|
||||
tools.StringToBytes(pwd)...,
|
||||
)...,
|
||||
)); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = io.ReadFull(conn, readCon[:2]); err != nil {
|
||||
return
|
||||
}
|
||||
switch readCon[1] {
|
||||
case 0:
|
||||
default:
|
||||
err = errors.New("socks5 auth error")
|
||||
return
|
||||
}
|
||||
case 0:
|
||||
default:
|
||||
err = errors.New("not support auth format")
|
||||
return
|
||||
}
|
||||
|
||||
host := remoteUrl.Hostname()
|
||||
var port int
|
||||
if cport := remoteUrl.Port(); cport == "" {
|
||||
switch remoteUrl.Scheme {
|
||||
case "http":
|
||||
port = 80
|
||||
case "https":
|
||||
port = 443
|
||||
case "socks5":
|
||||
port = 1080
|
||||
default:
|
||||
err = errors.New("not support scheme")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
port, err = strconv.Atoi(cport)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
writeCon := []byte{5, 1, 0}
|
||||
ip, ipInt := gtls.ParseHost(host)
|
||||
switch ipInt {
|
||||
case 4:
|
||||
writeCon = append(writeCon, 1)
|
||||
writeCon = append(writeCon, ip...)
|
||||
case 6:
|
||||
writeCon = append(writeCon, 4)
|
||||
writeCon = append(writeCon, ip...)
|
||||
case 0:
|
||||
if len(host) > 255 {
|
||||
err = errors.New("FQDN too long")
|
||||
return
|
||||
}
|
||||
writeCon = append(writeCon, 3)
|
||||
writeCon = append(writeCon, byte(len(host)))
|
||||
writeCon = append(writeCon, host...)
|
||||
}
|
||||
writeCon = append(writeCon, byte(port>>8), byte(port))
|
||||
if _, err = conn.Write(writeCon); err != nil {
|
||||
remoteAddr.NetWork = network
|
||||
err = WriteUdpAddr(conn, remoteAddr)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
readCon := make([]byte, 3)
|
||||
if _, err = io.ReadFull(conn, readCon); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -386,32 +314,92 @@ func (obj *Dialer) clientVerifySocks5(conn net.Conn, proxyUrl *url.URL, remoteUr
|
||||
err = errors.New("socks conn error")
|
||||
return
|
||||
}
|
||||
if readCon[3] != 1 {
|
||||
err = errors.New("socks conn type error")
|
||||
proxyAddress, err = ReadUdpAddr(conn)
|
||||
return
|
||||
}
|
||||
func (obj *Dialer) verifyTCPSocks5(conn net.Conn, proxyAddr Address, remoteAddr Address) (err error) {
|
||||
_, err = obj.verifySocks5(conn, "tcp", proxyAddr, remoteAddr)
|
||||
return
|
||||
}
|
||||
|
||||
func (obj *Dialer) verifyUDPSocks5(ctx context.Context, conn net.Conn, proxyAddr Address, remoteAddr Address) (wrapConn net.PacketConn, err error) {
|
||||
remoteAddr.NetWork = "udp"
|
||||
proxyAddress, err := obj.verifySocks5(conn, "udp", proxyAddr, remoteAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var listener net.ListenConfig
|
||||
wrapConn, err = listener.ListenPacket(ctx, "udp", ":0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
wrapConn, err = NewUDPConn(wrapConn, &net.UDPAddr{IP: proxyAddress.IP, Port: proxyAddress.Port})
|
||||
if err != nil {
|
||||
return wrapConn, err
|
||||
}
|
||||
go func() {
|
||||
var buf [1]byte
|
||||
for {
|
||||
_, err := conn.Read(buf[:])
|
||||
if err != nil {
|
||||
wrapConn.Close()
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
return wrapConn, nil
|
||||
}
|
||||
func (obj *Dialer) writeCmd(conn net.Conn, network string) (err error) {
|
||||
var cmd byte
|
||||
switch network {
|
||||
case "tcp":
|
||||
cmd = 1
|
||||
case "udp":
|
||||
cmd = 3
|
||||
default:
|
||||
return errors.New("not support network")
|
||||
}
|
||||
_, err = conn.Write([]byte{5, cmd, 0})
|
||||
return
|
||||
}
|
||||
func (obj *Dialer) verifySocks5Auth(conn net.Conn, proxyAddr Address) (err error) {
|
||||
if _, err = conn.Write([]byte{5, 2, 0, 2}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch readCon[3] {
|
||||
case 1: //ipv4
|
||||
readCon := make([]byte, 2)
|
||||
if _, err = io.ReadFull(conn, readCon); err != nil {
|
||||
return
|
||||
}
|
||||
switch readCon[1] {
|
||||
case 2:
|
||||
if proxyAddr.User == "" || proxyAddr.Password == "" {
|
||||
err = errors.New("socks5 need auth")
|
||||
return
|
||||
}
|
||||
if _, err = conn.Write(append(
|
||||
append(
|
||||
[]byte{1, byte(len(proxyAddr.User))},
|
||||
tools.StringToBytes(proxyAddr.User)...,
|
||||
),
|
||||
append(
|
||||
[]byte{byte(len(proxyAddr.Password))},
|
||||
tools.StringToBytes(proxyAddr.Password)...,
|
||||
)...,
|
||||
)); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = io.ReadFull(conn, readCon); err != nil {
|
||||
return
|
||||
}
|
||||
case 3: //domain
|
||||
if _, err = io.ReadFull(conn, readCon[:1]); err != nil {
|
||||
return
|
||||
}
|
||||
if _, err = io.ReadFull(conn, make([]byte, readCon[0])); err != nil {
|
||||
return
|
||||
}
|
||||
case 4: //IPv6
|
||||
if _, err = io.ReadFull(conn, make([]byte, 16)); err != nil {
|
||||
return
|
||||
switch readCon[1] {
|
||||
case 0:
|
||||
default:
|
||||
err = errors.New("socks5 auth error")
|
||||
}
|
||||
case 0:
|
||||
default:
|
||||
err = errors.New("invalid atyp")
|
||||
return
|
||||
err = errors.New("not support auth format")
|
||||
}
|
||||
_, err = io.ReadFull(conn, readCon[:2])
|
||||
return
|
||||
}
|
||||
func (obj *Dialer) lookupIPAddr(ips []net.IPAddr, addrType gtls.AddrType) (net.IP, error) {
|
||||
@@ -431,10 +419,6 @@ func (obj *Dialer) lookupIPAddr(ips []net.IPAddr, addrType gtls.AddrType) (net.I
|
||||
}
|
||||
return nil, errors.New("dns parse host error")
|
||||
}
|
||||
|
||||
// func (obj *Dialer) getDialer(option *RequestOption) *net.Dialer {
|
||||
// return newDialer(option.DialOption)
|
||||
// }
|
||||
func (obj *Dialer) addTls(ctx context.Context, conn net.Conn, host string, forceHttp1 bool, tlsConfig *tls.Config) (*tls.Conn, error) {
|
||||
var tlsConn *tls.Conn
|
||||
tlsConfig.ServerName = gtls.GetServerName(host)
|
||||
@@ -455,8 +439,8 @@ func (obj *Dialer) addJa3Tls(ctx context.Context, conn net.Conn, host string, fo
|
||||
}
|
||||
return ja3.NewClient(ctx, conn, ja3Spec, forceHttp1, tlsConfig)
|
||||
}
|
||||
func (obj *Dialer) Socks5Proxy(ctx context.Context, ctxData *RequestOption, network string, proxyUrl *url.URL, remoteUrl *url.URL) (conn net.Conn, err error) {
|
||||
if conn, err = obj.DialContext(ctx, ctxData, network, net.JoinHostPort(proxyUrl.Hostname(), proxyUrl.Port())); err != nil {
|
||||
func (obj *Dialer) Socks5TcpProxy(ctx context.Context, ctxData *RequestOption, proxyAddr Address, remoteAddr Address) (conn net.Conn, err error) {
|
||||
if conn, err = obj.DialContext(ctx, ctxData, "tcp", proxyAddr); err != nil {
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
@@ -466,8 +450,8 @@ func (obj *Dialer) Socks5Proxy(ctx context.Context, ctxData *RequestOption, netw
|
||||
}()
|
||||
didVerify := make(chan struct{})
|
||||
go func() {
|
||||
err = obj.clientVerifySocks5(conn, proxyUrl, remoteUrl)
|
||||
close(didVerify)
|
||||
defer close(didVerify)
|
||||
err = obj.verifyTCPSocks5(conn, proxyAddr, remoteAddr)
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -476,6 +460,41 @@ func (obj *Dialer) Socks5Proxy(ctx context.Context, ctxData *RequestOption, netw
|
||||
return
|
||||
}
|
||||
}
|
||||
func (obj *Dialer) Socks5UdpProxy(ctx context.Context, ctxData *RequestOption, proxyAddress Address, remoteAddress Address) (udpConn net.PacketConn, err error) {
|
||||
conn, err := obj.ProxyDialContext(ctx, ctxData, "tcp", proxyAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
if udpConn != nil {
|
||||
udpConn.Close()
|
||||
}
|
||||
}
|
||||
}()
|
||||
didVerify := make(chan struct{})
|
||||
go func() {
|
||||
defer close(didVerify)
|
||||
udpConn, err = obj.verifyUDPSocks5(ctx, conn, proxyAddress, remoteAddress)
|
||||
if ctxData.Logger != nil {
|
||||
ctxData.Logger(Log{
|
||||
Id: ctxData.requestId,
|
||||
Time: time.Now(),
|
||||
Type: LogType_ProxyConnectRemote,
|
||||
Msg: remoteAddress.String(),
|
||||
})
|
||||
}
|
||||
}()
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return udpConn, context.Cause(ctx)
|
||||
case <-didVerify:
|
||||
return
|
||||
}
|
||||
}
|
||||
func (obj *Dialer) clientVerifyHttps(ctx context.Context, conn net.Conn, proxyUrl *url.URL, remoteUrl *url.URL) (err error) {
|
||||
hdr := make(http.Header)
|
||||
hdr.Set("User-Agent", tools.UserAgent)
|
||||
@@ -492,6 +511,7 @@ func (obj *Dialer) clientVerifyHttps(ctx context.Context, conn net.Conn, proxyUr
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
connectReq.Header = hdr
|
||||
connectReq.Host = remoteUrl.Host
|
||||
if err = connectReq.Write(conn); err != nil {
|
||||
|
||||
17
go.mod
17
go.mod
@@ -10,23 +10,26 @@ require (
|
||||
github.com/gospider007/http2 v0.0.0-20241222151842-034aa1d46e9d
|
||||
github.com/gospider007/http3 v0.0.0-20241215120136-980caa047c47
|
||||
github.com/gospider007/ja3 v0.0.0-20241216123149-83352be79439
|
||||
github.com/gospider007/proxy v0.0.0-20241224001512-8d6d724b8ca3
|
||||
github.com/gospider007/re v0.0.0-20241216142712-efbef8d55ea2
|
||||
github.com/gospider007/tools v0.0.0-20241216141313-4a832f55a843
|
||||
github.com/gospider007/websocket v0.0.0-20241216130619-89829336d9a6
|
||||
github.com/quic-go/quic-go v0.48.2
|
||||
github.com/refraction-networking/uquic v0.0.6
|
||||
github.com/refraction-networking/utls v1.6.7
|
||||
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67
|
||||
golang.org/x/net v0.33.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/PuerkitoBio/goquery v1.10.0 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.10.1 // indirect
|
||||
github.com/STARRY-S/zip v0.2.1 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.0 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/caddyserver/certmagic v0.21.4 // indirect
|
||||
github.com/caddyserver/certmagic v0.21.5 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/cloudflare/circl v1.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
@@ -37,6 +40,7 @@ require (
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect
|
||||
github.com/gospider007/blog v0.0.0-20241205091827-6bcaf48620d4 // indirect
|
||||
github.com/gospider007/kinds v0.0.0-20240929092451-8f867acde255 // indirect
|
||||
github.com/gospider007/net v0.0.0-20241216130419-175071962ced // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
@@ -45,17 +49,15 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/libdns/libdns v0.2.2 // indirect
|
||||
github.com/mholt/acmez/v2 v2.0.3 // indirect
|
||||
github.com/mholt/archives v0.0.0-20241216060121-23e0af8fe73d // indirect
|
||||
github.com/mholt/acmez/v3 v3.0.0 // indirect
|
||||
github.com/mholt/archives v0.0.0-20241226194006-fc8400ac3529 // indirect
|
||||
github.com/miekg/dns v1.1.62 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.0.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.22.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.48.2 // indirect
|
||||
github.com/refraction-networking/uquic v0.0.6 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.5 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
@@ -68,6 +70,7 @@ require (
|
||||
go.uber.org/mock v0.5.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/image v0.23.0 // indirect
|
||||
|
||||
34
go.sum
34
go.sum
@@ -17,8 +17,8 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
|
||||
github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
|
||||
github.com/PuerkitoBio/goquery v1.10.1 h1:Y8JGYUkXWTGRB6Ars3+j3kN0xg1YqqlwvdTV8WTFQcU=
|
||||
github.com/PuerkitoBio/goquery v1.10.1/go.mod h1:IYiHrOMps66ag56LEH7QYDDupKXyo5A8qrjIx3ZtujY=
|
||||
github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg=
|
||||
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
@@ -31,8 +31,8 @@ github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A
|
||||
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
|
||||
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||
github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
|
||||
github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
|
||||
github.com/caddyserver/certmagic v0.21.5 h1:iIga4nZRgd27EIEbX7RZmoRMul+EVBn/h7bAGL83dnY=
|
||||
github.com/caddyserver/certmagic v0.21.5/go.mod h1:n1sCo7zV1Ez2j+89wrzDxo4N/T1Ws/Vx8u5NvuBFabw=
|
||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
||||
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -113,6 +113,10 @@ github.com/gospider007/ja3 v0.0.0-20241216123149-83352be79439 h1:zDuskJWFKk7JMs7
|
||||
github.com/gospider007/ja3 v0.0.0-20241216123149-83352be79439/go.mod h1:AZIKhClKQolSI8o6ixFKIVGOC4lWOdsrF4fshR5axO4=
|
||||
github.com/gospider007/kinds v0.0.0-20240929092451-8f867acde255 h1:X+AM/mgmh/EfyQUjKZp1VFc9TSlrhkwS0eSYeo5fMs4=
|
||||
github.com/gospider007/kinds v0.0.0-20240929092451-8f867acde255/go.mod h1:yZx7Zfp1I4P6CO3TcDyDY5SuXQYr0bZjzT9zG0XrJAI=
|
||||
github.com/gospider007/net v0.0.0-20241216130419-175071962ced h1:vQfZkebsHLjxDk9Wg3neaIsgtyARNmkgrHsh+WroAVY=
|
||||
github.com/gospider007/net v0.0.0-20241216130419-175071962ced/go.mod h1:GrNK3zEmo7N9nuAcOOu4GFw+kvfkYaOMiRSOm38CH7o=
|
||||
github.com/gospider007/proxy v0.0.0-20241224001512-8d6d724b8ca3 h1:lc25EnLnhcXk0hmBvmHYh+pOAJoWvdTFVAVG/JWDSiw=
|
||||
github.com/gospider007/proxy v0.0.0-20241224001512-8d6d724b8ca3/go.mod h1:Zwwc4Sxsy2MIh9V3Bvpj9JXnqJKkLz6TRyJRKIUgqDg=
|
||||
github.com/gospider007/re v0.0.0-20241216142712-efbef8d55ea2 h1:ixXFS1DqP0NnHna+b0JKaPqMRYRmahzUADZn7PawQq0=
|
||||
github.com/gospider007/re v0.0.0-20241216142712-efbef8d55ea2/go.mod h1:kr9bUaC42FS019Ak23fSctbTRB2JpfPPg/pSVjQmsws=
|
||||
github.com/gospider007/tools v0.0.0-20241216141313-4a832f55a843 h1:Q5judZjpY0p9Qui/ovk2vpvunc1wGkk0wgc8ApbTi3U=
|
||||
@@ -147,10 +151,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
|
||||
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
||||
github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
|
||||
github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
|
||||
github.com/mholt/archives v0.0.0-20241216060121-23e0af8fe73d h1:Vw3f39TqFSQLA+OyW+8SouppHTYzX8/fDv6Ao8uj3Ho=
|
||||
github.com/mholt/archives v0.0.0-20241216060121-23e0af8fe73d/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I=
|
||||
github.com/mholt/acmez/v3 v3.0.0 h1:r1NcjuWR0VaKP2BTjDK9LRFBw/WvURx3jlaEUl9Ht8E=
|
||||
github.com/mholt/acmez/v3 v3.0.0/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
||||
github.com/mholt/archives v0.0.0-20241226194006-fc8400ac3529 h1:XsFbmbdHgEXRCASoX0wlUi1Es+yTDhsmiUo2UVukmLs=
|
||||
github.com/mholt/archives v0.0.0-20241226194006-fc8400ac3529/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I=
|
||||
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -160,10 +164,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nwaples/rardecode/v2 v2.0.1 h1:3MN6/R+Y4c7e+21U3yhWuUcf72sYmcmr6jtiuAVSH1A=
|
||||
github.com/nwaples/rardecode/v2 v2.0.1/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
||||
github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM=
|
||||
github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM=
|
||||
github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw=
|
||||
github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
|
||||
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
|
||||
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
|
||||
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@@ -229,6 +233,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -434,8 +440,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk=
|
||||
google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
|
||||
@@ -44,6 +44,7 @@ type Log struct {
|
||||
|
||||
// Connection Management Options
|
||||
type ClientOption struct {
|
||||
RequestOption
|
||||
Logger func(Log) //debuggable
|
||||
H3 bool //开启http3
|
||||
OrderHeaders []string //order headers
|
||||
|
||||
@@ -35,7 +35,7 @@ func GetRequestOption(ctx context.Context) *RequestOption {
|
||||
if ok {
|
||||
return option
|
||||
}
|
||||
return nil
|
||||
return new(RequestOption)
|
||||
}
|
||||
|
||||
// sends a GET request and returns the response.
|
||||
|
||||
@@ -15,6 +15,8 @@ import (
|
||||
"github.com/gospider007/http2"
|
||||
"github.com/gospider007/http3"
|
||||
"github.com/gospider007/tools"
|
||||
"github.com/quic-go/quic-go"
|
||||
uquic "github.com/refraction-networking/uquic"
|
||||
)
|
||||
|
||||
type reqTask struct {
|
||||
@@ -89,14 +91,38 @@ func (obj *roundTripper) newConnecotr() *connecotr {
|
||||
return conne
|
||||
}
|
||||
|
||||
func (obj *roundTripper) http3Dial(option *RequestOption, req *http.Request) (conn *connecotr, err error) {
|
||||
func (obj *roundTripper) http3Dial(ctx context.Context, option *RequestOption, proxyUrl *url.URL, remtoeAddress Address) (udpConn net.PacketConn, err error) {
|
||||
if proxyUrl != nil {
|
||||
if proxyUrl.Scheme != "socks5" {
|
||||
err = errors.New("http3 only socks5 proxy supported")
|
||||
return
|
||||
}
|
||||
var proxyAddress Address
|
||||
proxyAddress, err = GetAddressWithUrl(proxyUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
udpConn, err = obj.dialer.Socks5UdpProxy(ctx, option, proxyAddress, remtoeAddress)
|
||||
} else {
|
||||
udpConn, err = net.ListenUDP("udp", nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
func (obj *roundTripper) ghttp3Dial(ctx context.Context, option *RequestOption, proxyUrl *url.URL, remoteAddress Address) (conn *connecotr, err error) {
|
||||
udpConn, err := obj.http3Dial(ctx, option, proxyUrl, remoteAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig := option.TlsConfig.Clone()
|
||||
tlsConfig.NextProtos = []string{http3.NextProtoH3}
|
||||
tlsConfig.ServerName = req.Host
|
||||
netConn, err := http3.Dial(req.Context(), getAddr(req.URL), tlsConfig, nil)
|
||||
if err != nil {
|
||||
return
|
||||
tlsConfig.ServerName = remoteAddress.Host
|
||||
if remoteAddress.IP == nil {
|
||||
remoteAddress.IP, err = obj.dialer.loadHost(ctx, remoteAddress.Name, option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
netConn, err := quic.DialEarly(ctx, udpConn, &net.UDPAddr{IP: remoteAddress.IP, Port: remoteAddress.Port}, tlsConfig, nil)
|
||||
conn = obj.newConnecotr()
|
||||
conn.Conn = http3.NewClient(netConn, func() {
|
||||
conn.forceCnl(errors.New("http3 client close"))
|
||||
@@ -104,14 +130,21 @@ func (obj *roundTripper) http3Dial(option *RequestOption, req *http.Request) (co
|
||||
return
|
||||
}
|
||||
|
||||
func (obj *roundTripper) ghttp3Dial(option *RequestOption, req *http.Request) (conn *connecotr, err error) {
|
||||
func (obj *roundTripper) uhttp3Dial(ctx context.Context, option *RequestOption, proxyUrl *url.URL, remoteAddress Address) (conn *connecotr, err error) {
|
||||
udpConn, err := obj.http3Dial(ctx, option, proxyUrl, remoteAddress)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tlsConfig := option.UtlsConfig.Clone()
|
||||
tlsConfig.NextProtos = []string{http3.NextProtoH3}
|
||||
tlsConfig.ServerName = req.Host
|
||||
netConn, err := http3.UDial(req.Context(), getAddr(req.URL), tlsConfig, nil)
|
||||
if err != nil {
|
||||
return
|
||||
tlsConfig.ServerName = remoteAddress.Host
|
||||
if remoteAddress.IP == nil {
|
||||
remoteAddress.IP, err = obj.dialer.loadHost(ctx, remoteAddress.Name, option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
netConn, err := uquic.DialEarly(ctx, udpConn, remoteAddress, tlsConfig, nil)
|
||||
conn = obj.newConnecotr()
|
||||
conn.Conn = http3.NewUClient(netConn, func() {
|
||||
conn.forceCnl(errors.New("http3 client close"))
|
||||
@@ -120,22 +153,35 @@ func (obj *roundTripper) ghttp3Dial(option *RequestOption, req *http.Request) (c
|
||||
}
|
||||
|
||||
func (obj *roundTripper) dial(option *RequestOption, req *http.Request) (conn *connecotr, err error) {
|
||||
if option.H3 {
|
||||
if option.Ja3Spec.IsSet() {
|
||||
return obj.ghttp3Dial(option, req)
|
||||
} else {
|
||||
return obj.http3Dial(option, req)
|
||||
}
|
||||
}
|
||||
proxys, err := obj.initProxys(option, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if option.H3 {
|
||||
var proxyUrl *url.URL
|
||||
if len(proxys) > 0 {
|
||||
proxyUrl = proxys[0]
|
||||
}
|
||||
remoteAddress, err := GetAddressWithUrl(req.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if option.Ja3Spec.IsSet() {
|
||||
return obj.uhttp3Dial(req.Context(), option, proxyUrl, remoteAddress)
|
||||
} else {
|
||||
return obj.ghttp3Dial(req.Context(), option, proxyUrl, remoteAddress)
|
||||
}
|
||||
}
|
||||
var netConn net.Conn
|
||||
if len(proxys) > 0 {
|
||||
netConn, err = obj.dialer.DialProxyContext(req.Context(), option, "tcp", option.TlsConfig.Clone(), append(proxys, cloneUrl(req.URL))...)
|
||||
} else {
|
||||
netConn, err = obj.dialer.DialContext(req.Context(), option, "tcp", getAddr(req.URL))
|
||||
var remoteAddress Address
|
||||
remoteAddress, err = GetAddressWithUrl(req.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
netConn, err = obj.dialer.DialContext(req.Context(), option, "tcp", remoteAddress)
|
||||
}
|
||||
defer func() {
|
||||
if err != nil && netConn != nil {
|
||||
|
||||
307
socks5.go
Normal file
307
socks5.go
Normal file
@@ -0,0 +1,307 @@
|
||||
package requests
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
errBadHeader = errors.New("bad header")
|
||||
errUnsupportedMethod = errors.New("unsupported method")
|
||||
)
|
||||
|
||||
const MaxUdpPacket int = math.MaxUint16 - 28
|
||||
const (
|
||||
ipv4Address = 0x01
|
||||
fqdnAddress = 0x03
|
||||
ipv6Address = 0x04
|
||||
)
|
||||
|
||||
func WriteUdpAddr(w io.Writer, addr Address) error {
|
||||
if addr.IP != nil {
|
||||
if ip4 := addr.IP.To4(); ip4 != nil {
|
||||
_, err := w.Write([]byte{ipv4Address})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(ip4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else if ip6 := addr.IP.To16(); ip6 != nil {
|
||||
_, err := w.Write([]byte{ipv6Address})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write(ip6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
_, err := w.Write([]byte{ipv4Address, 0, 0, 0, 0})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if addr.Name != "" {
|
||||
if len(addr.Name) > 255 {
|
||||
return errors.New("errStringTooLong")
|
||||
}
|
||||
_, err := w.Write([]byte{fqdnAddress, byte(len(addr.Name))})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = w.Write([]byte(addr.Name))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
_, err := w.Write([]byte{ipv4Address, 0, 0, 0, 0})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var p [2]byte
|
||||
binary.BigEndian.PutUint16(p[:], uint16(addr.Port))
|
||||
_, err := w.Write(p[:])
|
||||
return err
|
||||
}
|
||||
|
||||
type Address struct {
|
||||
User string
|
||||
Password string
|
||||
Name string
|
||||
Host string
|
||||
IP net.IP
|
||||
Port int
|
||||
NetWork string
|
||||
}
|
||||
|
||||
func (a Address) String() string {
|
||||
port := strconv.Itoa(a.Port)
|
||||
if len(a.IP) != 0 {
|
||||
return net.JoinHostPort(a.IP.String(), port)
|
||||
}
|
||||
return net.JoinHostPort(a.Name, port)
|
||||
}
|
||||
func (a Address) Network() string {
|
||||
return a.NetWork
|
||||
}
|
||||
|
||||
func ReadUdpAddr(r io.Reader) (Address, error) {
|
||||
UdpAddress := Address{}
|
||||
var addrType [1]byte
|
||||
if _, err := r.Read(addrType[:]); err != nil {
|
||||
return UdpAddress, err
|
||||
}
|
||||
|
||||
switch addrType[0] {
|
||||
case ipv4Address:
|
||||
addr := make(net.IP, net.IPv4len)
|
||||
if _, err := io.ReadFull(r, addr); err != nil {
|
||||
return UdpAddress, err
|
||||
}
|
||||
UdpAddress.IP = addr
|
||||
case ipv6Address:
|
||||
addr := make(net.IP, net.IPv6len)
|
||||
if _, err := io.ReadFull(r, addr); err != nil {
|
||||
return UdpAddress, err
|
||||
}
|
||||
UdpAddress.IP = addr
|
||||
case fqdnAddress:
|
||||
if _, err := r.Read(addrType[:]); err != nil {
|
||||
return UdpAddress, err
|
||||
}
|
||||
addrLen := int(addrType[0])
|
||||
fqdn := make([]byte, addrLen)
|
||||
if _, err := io.ReadFull(r, fqdn); err != nil {
|
||||
return UdpAddress, err
|
||||
}
|
||||
UdpAddress.Name = string(fqdn)
|
||||
default:
|
||||
return UdpAddress, errors.New("invalid atyp")
|
||||
}
|
||||
var port [2]byte
|
||||
if _, err := io.ReadFull(r, port[:]); err != nil {
|
||||
return UdpAddress, err
|
||||
}
|
||||
UdpAddress.Port = int(binary.BigEndian.Uint16(port[:]))
|
||||
return UdpAddress, nil
|
||||
}
|
||||
|
||||
type UDPConn struct {
|
||||
bufRead [MaxUdpPacket]byte
|
||||
bufWrite [MaxUdpPacket]byte
|
||||
proxyAddress net.Addr
|
||||
// defaultTarget net.Addr
|
||||
prefix []byte
|
||||
net.PacketConn
|
||||
}
|
||||
|
||||
func NewUDPConn(raw net.PacketConn, proxyAddress net.Addr) (*UDPConn, error) {
|
||||
conn := &UDPConn{
|
||||
PacketConn: raw,
|
||||
proxyAddress: proxyAddress,
|
||||
prefix: []byte{0, 0, 0},
|
||||
}
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// func NewUDPConn(raw net.PacketConn, proxyAddress net.Addr, defaultTarget net.Addr) (*UDPConn, error) {
|
||||
// conn := &UDPConn{
|
||||
// PacketConn: raw,
|
||||
// proxyAddress: proxyAddress,
|
||||
// defaultTarget: defaultTarget,
|
||||
// prefix: []byte{0, 0, 0},
|
||||
// }
|
||||
// return conn, nil
|
||||
// }
|
||||
|
||||
// ReadFrom implements the net.PacketConn ReadFrom method.
|
||||
func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
|
||||
n, addr, err = c.PacketConn.ReadFrom(c.bufRead[:])
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if n < len(c.prefix) || addr.String() != c.proxyAddress.String() {
|
||||
return 0, nil, errBadHeader
|
||||
}
|
||||
buf := bytes.NewBuffer(c.bufRead[len(c.prefix):n])
|
||||
a, err := ReadUdpAddr(buf)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
n = copy(p, buf.Bytes())
|
||||
return n, a, nil
|
||||
}
|
||||
|
||||
// WriteTo implements the net.PacketConn WriteTo method.
|
||||
func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
|
||||
buf := bytes.NewBuffer(c.bufWrite[:0])
|
||||
buf.Write(c.prefix)
|
||||
udpAddr := addr.(*net.UDPAddr)
|
||||
err = WriteUdpAddr(buf, Address{IP: udpAddr.IP, Port: udpAddr.Port})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n, err = buf.Write(p)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
data := buf.Bytes()
|
||||
_, err = c.PacketConn.WriteTo(data, c.proxyAddress)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// // Read implements the net.Conn Read method.
|
||||
// func (c *UDPConn) Read(b []byte) (int, error) {
|
||||
// n, addr, err := c.ReadFrom(b)
|
||||
// if err != nil {
|
||||
// return 0, err
|
||||
// }
|
||||
// if addr.String() != c.defaultTarget.String() {
|
||||
// return c.Read(b)
|
||||
// }
|
||||
// return n, nil
|
||||
// }
|
||||
|
||||
// // Write implements the net.Conn Write method.
|
||||
// func (c *UDPConn) Write(b []byte) (int, error) {
|
||||
// return c.WriteTo(b, c.defaultTarget)
|
||||
// }
|
||||
|
||||
// // RemoteAddr implements the net.Conn RemoteAddr method.
|
||||
// func (c *UDPConn) RemoteAddr() net.Addr {
|
||||
// return c.defaultTarget
|
||||
// }
|
||||
|
||||
// SetReadBuffer implements the net.UDPConn SetReadBuffer method.
|
||||
func (c *UDPConn) SetReadBuffer(bytes int) error {
|
||||
udpConn, ok := c.PacketConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return errUnsupportedMethod
|
||||
}
|
||||
return udpConn.SetReadBuffer(bytes)
|
||||
}
|
||||
|
||||
// SetWriteBuffer implements the net.UDPConn SetWriteBuffer method.
|
||||
func (c *UDPConn) SetWriteBuffer(bytes int) error {
|
||||
udpConn, ok := c.PacketConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return errUnsupportedMethod
|
||||
}
|
||||
return udpConn.SetWriteBuffer(bytes)
|
||||
}
|
||||
|
||||
// SetDeadline implements the Conn SetDeadline method.
|
||||
func (c *UDPConn) SetDeadline(t time.Time) error {
|
||||
udpConn, ok := c.PacketConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return errUnsupportedMethod
|
||||
}
|
||||
return udpConn.SetDeadline(t)
|
||||
}
|
||||
|
||||
// SetReadDeadline implements the Conn SetReadDeadline method.
|
||||
func (c *UDPConn) SetReadDeadline(t time.Time) error {
|
||||
udpConn, ok := c.PacketConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return errUnsupportedMethod
|
||||
}
|
||||
return udpConn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
// SetWriteDeadline implements the Conn SetWriteDeadline method.
|
||||
func (c *UDPConn) SetWriteDeadline(t time.Time) error {
|
||||
udpConn, ok := c.PacketConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return errUnsupportedMethod
|
||||
}
|
||||
return udpConn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
// ReadFromUDP implements the net.UDPConn ReadFromUDP method.
|
||||
func (c *UDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
|
||||
udpConn, ok := c.PacketConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return 0, nil, errUnsupportedMethod
|
||||
}
|
||||
return udpConn.ReadFromUDP(b)
|
||||
}
|
||||
|
||||
// ReadMsgUDP implements the net.UDPConn ReadMsgUDP method.
|
||||
func (c *UDPConn) ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) {
|
||||
udpConn, ok := c.PacketConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return 0, 0, 0, nil, errUnsupportedMethod
|
||||
}
|
||||
return udpConn.ReadMsgUDP(b, oob)
|
||||
}
|
||||
|
||||
// WriteToUDP implements the net.UDPConn WriteToUDP method.
|
||||
func (c *UDPConn) WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) {
|
||||
udpConn, ok := c.PacketConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return 0, errUnsupportedMethod
|
||||
}
|
||||
return udpConn.WriteToUDP(b, addr)
|
||||
}
|
||||
|
||||
// WriteMsgUDP implements the net.UDPConn WriteMsgUDP method.
|
||||
func (c *UDPConn) WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) {
|
||||
udpConn, ok := c.PacketConn.(*net.UDPConn)
|
||||
if !ok {
|
||||
return 0, 0, errUnsupportedMethod
|
||||
}
|
||||
return udpConn.WriteMsgUDP(b, oob, addr)
|
||||
}
|
||||
94
test/proxy/http3_proxy_test.go
Normal file
94
test/proxy/http3_proxy_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gospider007/gtls"
|
||||
"github.com/gospider007/proxy"
|
||||
"github.com/gospider007/requests"
|
||||
"github.com/quic-go/quic-go/http3"
|
||||
)
|
||||
|
||||
var (
|
||||
proxyHost = "127.0.0.1:1080"
|
||||
remoteHost = "127.0.0.1:8080"
|
||||
)
|
||||
|
||||
func client() {
|
||||
for range 5 {
|
||||
resp, err := requests.Post(nil, "https://"+remoteHost, requests.RequestOption{
|
||||
H3: true,
|
||||
Logger: func(l requests.Log) {
|
||||
log.Print(l)
|
||||
},
|
||||
Proxy: "socks5://" + proxyHost,
|
||||
Body: []byte("hello, server!"),
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Println(resp.StatusCode())
|
||||
fmt.Println(resp.Text())
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func server() {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
fmt.Fprint(w, "hello, world!")
|
||||
case http.MethodPost:
|
||||
result, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fmt.Fprint(w, "error:", err.Error())
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "echo:'%s'", string(result))
|
||||
default:
|
||||
fmt.Fprint(w, "method is not supported")
|
||||
}
|
||||
})
|
||||
tlsCert, err := gtls.CreateProxyCertWithName("localhost")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
server := http3.Server{
|
||||
Addr: "0.0.0.0:8080",
|
||||
Handler: mux,
|
||||
TLSConfig: &tls.Config{
|
||||
Certificates: []tls.Certificate{tlsCert},
|
||||
NextProtos: []string{"http3-echo-example"},
|
||||
},
|
||||
}
|
||||
fmt.Println("Server is listening...")
|
||||
fmt.Println(server.ListenAndServe())
|
||||
}
|
||||
func proxyServer() {
|
||||
c, err := proxy.NewClient(nil, proxy.ClientOption{
|
||||
Addr: ":1080",
|
||||
Debug: true,
|
||||
DisVerify: true,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.Run()
|
||||
}
|
||||
|
||||
func TestHttp3Proxy(t *testing.T) {
|
||||
go server()
|
||||
go proxyServer()
|
||||
time.Sleep(time.Second * 3)
|
||||
client()
|
||||
}
|
||||
57
tools.go
57
tools.go
@@ -3,15 +3,18 @@ package requests
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/textproto"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
_ "unsafe"
|
||||
|
||||
"github.com/gospider007/gtls"
|
||||
"github.com/gospider007/ja3"
|
||||
"github.com/gospider007/tools"
|
||||
"golang.org/x/exp/slices"
|
||||
@@ -50,6 +53,60 @@ func getAddr(uurl *url.URL) (addr string) {
|
||||
}
|
||||
return uurl.Host
|
||||
}
|
||||
func GetAddressWithUrl(uurl *url.URL) (addr Address, err error) {
|
||||
if uurl == nil {
|
||||
return Address{}, errors.New("url is nil")
|
||||
}
|
||||
var port int
|
||||
portStr := uurl.Port()
|
||||
if portStr == "" {
|
||||
switch uurl.Scheme {
|
||||
case "http":
|
||||
port = 80
|
||||
case "https":
|
||||
port = 443
|
||||
case "socks5":
|
||||
port = 1080
|
||||
default:
|
||||
return Address{}, errors.New("unknown scheme")
|
||||
}
|
||||
} else {
|
||||
port, err = strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return Address{}, err
|
||||
}
|
||||
}
|
||||
ip, _ := gtls.ParseHost(uurl.Hostname())
|
||||
addr = Address{
|
||||
Name: uurl.Hostname(),
|
||||
Host: uurl.Host,
|
||||
IP: ip,
|
||||
Port: port,
|
||||
}
|
||||
if uurl.User != nil {
|
||||
addr.User = uurl.User.Username()
|
||||
addr.Password, _ = uurl.User.Password()
|
||||
}
|
||||
return
|
||||
}
|
||||
func GetAddressWithAddr(addrS string) (addr Address, err error) {
|
||||
host, port, err := net.SplitHostPort(addrS)
|
||||
if err != nil {
|
||||
return Address{}, err
|
||||
}
|
||||
ip, _ := gtls.ParseHost(host)
|
||||
portInt, err := strconv.Atoi(port)
|
||||
if err != nil {
|
||||
return Address{}, err
|
||||
}
|
||||
addr = Address{
|
||||
Name: host,
|
||||
IP: ip,
|
||||
Host: addrS,
|
||||
Port: portInt,
|
||||
}
|
||||
return
|
||||
}
|
||||
func cloneUrl(u *url.URL) *url.URL {
|
||||
if u == nil {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user