Skip to content

Commit 940458f

Browse files
committed
feat: add reflection based general client options to support gokhttp_ja3spoof transports
1 parent d5f0179 commit 940458f

File tree

3 files changed

+150
-5
lines changed

3 files changed

+150
-5
lines changed

client/opt_proxy.go

+83-3
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@ import (
44
"fmt"
55
"net/http"
66
"net/url"
7+
"reflect"
78
)
89

910
type ProxyOption struct {
1011
ProxyURL string
12+
proxyObj *url.URL
1113
}
1214

1315
func NewProxyOption(proxyURL string) *ProxyOption {
1416
return &ProxyOption{ProxyURL: proxyURL}
1517
}
1618

17-
// TODO: Allow for other types of proxies outside of stdlib too?
18-
19-
func (o *ProxyOption) Execute(client *http.Client) error {
19+
func (o *ProxyOption) ExecuteV1(client *http.Client) error {
2020
puo, err := url.Parse(o.ProxyURL)
2121
if err != nil {
2222
return fmt.Errorf("ProxyOption: url.Parse: %w", err)
@@ -29,3 +29,83 @@ func (o *ProxyOption) Execute(client *http.Client) error {
2929

3030
return nil
3131
}
32+
33+
func (o *ProxyOption) Execute(client *http.Client) error {
34+
puo, err := url.Parse(o.ProxyURL)
35+
if err != nil {
36+
return fmt.Errorf("ProxyOption: url.Parse: %w", err)
37+
}
38+
o.proxyObj = puo
39+
return o.processTransport(client, 1)
40+
}
41+
42+
func (o *ProxyOption) processTransport(clientOrTransport any, depth int) error {
43+
var (
44+
ok bool
45+
clientOrTransportRV reflect.Value
46+
)
47+
clientOrTransportRV, ok = clientOrTransport.(reflect.Value)
48+
if !ok {
49+
clientOrTransportRV = reflect.ValueOf(clientOrTransport)
50+
}
51+
52+
if clientOrTransportRV.Kind() != reflect.Ptr || clientOrTransportRV.IsNil() {
53+
return fmt.Errorf("expected non-nil pointer to struct")
54+
}
55+
56+
// fmt.Println(fmt.Sprintf("Depth %d clientOrTransport.Type: %s", depth, clientOrTransport.Type().String()))
57+
clientOrTransportElem := clientOrTransportRV.Elem()
58+
// fmt.Println(fmt.Sprintf("Depth %d rvElem.Type: %s", depth, clientOrTransportElem.Type().String()))
59+
60+
// http.Client, oohttp.Client, http.Transport, oohttp.StdlibTransport, oohttp.Transport are all structs.
61+
if clientOrTransportElem.Kind() != reflect.Struct {
62+
return fmt.Errorf("expected pointer to struct")
63+
}
64+
65+
// Check for ".Transport" field
66+
transportField := clientOrTransportElem.FieldByName("Transport")
67+
// Should always be an interface
68+
if transportField.Kind() == reflect.Interface {
69+
transportField = transportField.Elem()
70+
}
71+
72+
// If it exists, we should recurse into it
73+
if transportField.IsValid() {
74+
// fmt.Println(fmt.Sprintf("Depth %d configField.Type: %s", depth, transportField.Type().String()))
75+
if transportField.Kind() == reflect.Ptr {
76+
if transportField.IsNil() {
77+
return fmt.Errorf("transport field is nil")
78+
}
79+
return o.processTransport(transportField, depth+1)
80+
} else if transportField.Kind() == reflect.Struct {
81+
return o.processTransport(transportField.Addr(), depth+1)
82+
} else {
83+
return fmt.Errorf("transport field is not a struct or pointer to struct")
84+
}
85+
} else {
86+
// Set ".Proxy" field
87+
proxyField := clientOrTransportElem.FieldByName("Proxy")
88+
if proxyField.IsValid() && proxyField.Kind() == reflect.Func {
89+
if !proxyField.CanSet() {
90+
return fmt.Errorf("cannot set Proxy field")
91+
}
92+
// Literally make a new function based on it's signature
93+
proxyFieldType := proxyField.Type()
94+
newFuncValue := reflect.MakeFunc(proxyFieldType, func(args []reflect.Value) (results []reflect.Value) {
95+
// fmt.Println("Dynamic Proxy called with arguments:")
96+
for i, arg := range args {
97+
fmt.Printf(" Arg %d: %v\n", i, arg.Interface())
98+
}
99+
results = make([]reflect.Value, proxyFieldType.NumOut())
100+
results[0] = reflect.ValueOf(o.proxyObj)
101+
results[1] = reflect.Zero(reflect.TypeOf((*error)(nil)).Elem())
102+
return results
103+
})
104+
// Set the Proxy field to the new function
105+
proxyField.Set(newFuncValue)
106+
return nil
107+
} else {
108+
return fmt.Errorf("proxy field not found or not a function")
109+
}
110+
}
111+
}

client/opt_tls_config.go

+66-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io"
99
"net/http"
1010
"os"
11+
"reflect"
1112
"time"
1213

1314
"golang.org/x/net/http2"
@@ -46,7 +47,7 @@ type RawTLSConfigOption struct {
4647
Config *tls.Config
4748
}
4849

49-
func (opt *RawTLSConfigOption) Execute(client *http.Client) error {
50+
func (opt *RawTLSConfigOption) ExecuteV1(client *http.Client) error {
5051
typedH1Trans, ok := client.Transport.(*http.Transport)
5152
if ok {
5253
typedH1Trans.TLSClientConfig = opt.Config
@@ -58,11 +59,75 @@ func (opt *RawTLSConfigOption) Execute(client *http.Client) error {
5859
return nil
5960
}
6061

62+
func (opt *RawTLSConfigOption) Execute(client *http.Client) error {
63+
return opt.processTransport(client, 1)
64+
}
65+
6166
func (opt *RawTLSConfigOption) ExecuteTLSConfig(config *tls.Config) error {
6267
config = opt.Config
6368
return nil
6469
}
6570

71+
func (opt *RawTLSConfigOption) processTransport(clientOrTransport any, depth int) error {
72+
var (
73+
ok bool
74+
clientOrTransportRV reflect.Value
75+
)
76+
clientOrTransportRV, ok = clientOrTransport.(reflect.Value)
77+
if !ok {
78+
clientOrTransportRV = reflect.ValueOf(clientOrTransport)
79+
}
80+
81+
if clientOrTransportRV.Kind() != reflect.Ptr || clientOrTransportRV.IsNil() {
82+
return fmt.Errorf("expected non-nil pointer to struct")
83+
}
84+
85+
// fmt.Println(fmt.Sprintf("Depth %d clientOrTransport.Type: %s", depth, clientOrTransport.Type().String()))
86+
clientOrTransportElem := clientOrTransportRV.Elem()
87+
// fmt.Println(fmt.Sprintf("Depth %d rvElem.Type: %s", depth, clientOrTransportElem.Type().String()))
88+
89+
// http.Client, oohttp.Client, http.Transport, oohttp.StdlibTransport, oohttp.Transport are all structs.
90+
if clientOrTransportElem.Kind() != reflect.Struct {
91+
return fmt.Errorf("expected pointer to struct")
92+
}
93+
94+
// Check for ".Transport" field
95+
transportField := clientOrTransportElem.FieldByName("Transport")
96+
// Should always be an interface
97+
if transportField.Kind() == reflect.Interface {
98+
transportField = transportField.Elem()
99+
}
100+
101+
// If it exists, we should recurse into it
102+
if transportField.IsValid() {
103+
// fmt.Println(fmt.Sprintf("Depth %d configField.Type: %s", depth, transportField.Type().String()))
104+
if transportField.Kind() == reflect.Ptr {
105+
if transportField.IsNil() {
106+
return fmt.Errorf("transport field is nil")
107+
}
108+
return opt.processTransport(transportField, depth+1)
109+
} else if transportField.Kind() == reflect.Struct {
110+
return opt.processTransport(transportField.Addr(), depth+1)
111+
} else {
112+
return fmt.Errorf("transport field is not a struct or pointer to struct")
113+
}
114+
} else {
115+
// Set ".TLSClientConfig" field
116+
tlsConfigField := clientOrTransportElem.FieldByName("TLSClientConfig")
117+
if tlsConfigField.IsValid() {
118+
if !tlsConfigField.CanSet() {
119+
return fmt.Errorf("cannot set TLSClientConfig field")
120+
}
121+
122+
tlsConfigField.Set(reflect.ValueOf(opt.Config))
123+
124+
return nil
125+
} else {
126+
return fmt.Errorf("TLSClientConfig field not found")
127+
}
128+
}
129+
}
130+
66131
func NewRawTLSConfigOption(config *tls.Config) *RawTLSConfigOption {
67132
return &RawTLSConfigOption{Config: config}
68133
}

lib.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import (
44
"crypto/tls"
55
"crypto/x509"
66
"fmt"
7-
"github.com/BRUHItsABunny/gOkHttp/client"
7+
gokhttp_client "github.com/BRUHItsABunny/gOkHttp/client"
88
"net/http"
99
"os"
1010
)

0 commit comments

Comments
 (0)