This commit is contained in:
gospider
2025-01-02 17:19:55 +08:00
parent a8b19ecde0
commit 233bd0e503
9 changed files with 742 additions and 208 deletions

356
dial.go
View File

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

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

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

View File

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

View File

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

View File

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

View 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()
}

View File

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