feat: impersonate browser TLS fingerprints (#824)
This commit is contained in:
parent
c4136efef0
commit
eab73a3b19
18 changed files with 367 additions and 19 deletions
11
.github/workflows/ci.yml
vendored
11
.github/workflows/ci.yml
vendored
|
|
@ -27,7 +27,16 @@ jobs:
|
|||
go-version: stable
|
||||
|
||||
- name: Test
|
||||
run: go test -v ./...
|
||||
if: ${{ matrix.os != 'windows-11-arm' }}
|
||||
run: |
|
||||
go test -race -v ./...
|
||||
go test -race -v -tags no_spoof_tls_fingerprint ./...
|
||||
# -race is not supported on windows/arm64.
|
||||
- name: Test
|
||||
if: ${{ matrix.os == 'windows-11-arm' }}
|
||||
run: |
|
||||
go test -v ./...
|
||||
go test -v -tags no_spoof_tls_fingerprint ./...
|
||||
|
||||
- name: Build
|
||||
run: go build -trimpath -ldflags=-s .
|
||||
|
|
|
|||
11
go.mod
11
go.mod
|
|
@ -11,6 +11,8 @@ require (
|
|||
github.com/ayn2op/arikawa/v3 v3.0.0-20260708075022-9cc760fe507e
|
||||
github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6
|
||||
github.com/ayn2op/tview v0.0.0-20260803165347-d8123f4ec53c
|
||||
github.com/bogdanfinn/fhttp v0.6.8
|
||||
github.com/bogdanfinn/tls-client v1.15.1
|
||||
github.com/gdamore/tcell/v3 v3.4.1
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/uuid v1.6.0
|
||||
|
|
@ -28,6 +30,11 @@ require (
|
|||
|
||||
require (
|
||||
github.com/akavel/rsrc v0.10.2 // indirect
|
||||
github.com/bdandy/go-errors v1.2.2 // indirect
|
||||
github.com/bdandy/go-socks4 v1.2.3 // indirect
|
||||
github.com/bogdanfinn/quic-go-utls v1.0.9-utls // indirect
|
||||
github.com/bogdanfinn/utls v1.7.7-barnius // indirect
|
||||
github.com/bogdanfinn/websocket v1.5.5-barnius // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/danieljoos/wincred v1.2.3 // indirect
|
||||
|
|
@ -40,13 +47,17 @@ require (
|
|||
github.com/josephspurrier/goversioninfo v1.7.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.4.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/randall77/makefat v0.0.0-20260406194835-1b91746796b7 // indirect
|
||||
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 // indirect
|
||||
github.com/twmb/murmur3 v1.1.8 // indirect
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f // indirect
|
||||
golang.design/x/x11 v0.2.0 // indirect
|
||||
golang.org/x/crypto v0.54.0 // indirect
|
||||
golang.org/x/exp/shiny v0.0.0-20260727155853-b88d891fe743 // indirect
|
||||
golang.org/x/image v0.44.0 // indirect
|
||||
golang.org/x/mobile v0.0.0-20260730202154-c700fe717e6e // indirect
|
||||
golang.org/x/net v0.57.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/term v0.45.0 // indirect
|
||||
golang.org/x/text v0.40.0 // indirect
|
||||
|
|
|
|||
27
go.sum
27
go.sum
|
|
@ -16,6 +16,20 @@ github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6 h1:/khHm6G+GWwX
|
|||
github.com/ayn2op/ningen/v3 v3.0.1-0.20260708075321-0ba1ffc70bd6/go.mod h1:vqLIvETLkjc2vIgQzVEBqpGNCrgNe7YJqS9TUAJsXRI=
|
||||
github.com/ayn2op/tview v0.0.0-20260803165347-d8123f4ec53c h1:ApOfdF/YBJ+oy5zG5yJW/IbgCOoWcfJ/qukTnfFlUXc=
|
||||
github.com/ayn2op/tview v0.0.0-20260803165347-d8123f4ec53c/go.mod h1:bJZdl/FfspPLEW3nuX6ubt7YbmDoSj7f2IoWH37w1NE=
|
||||
github.com/bdandy/go-errors v1.2.2 h1:WdFv/oukjTJCLa79UfkGmwX7ZxONAihKu4V0mLIs11Q=
|
||||
github.com/bdandy/go-errors v1.2.2/go.mod h1:NkYHl4Fey9oRRdbB1CoC6e84tuqQHiqrOcZpqFEkBxM=
|
||||
github.com/bdandy/go-socks4 v1.2.3 h1:Q6Y2heY1GRjCtHbmlKfnwrKVU/k81LS8mRGLRlmDlic=
|
||||
github.com/bdandy/go-socks4 v1.2.3/go.mod h1:98kiVFgpdogR8aIGLWLvjDVZ8XcKPsSI/ypGrO+bqHI=
|
||||
github.com/bogdanfinn/fhttp v0.6.8 h1:LiQyHOY3i0QoxxNB7nq27/nGNNbtPj0fuBPozhR7Ws4=
|
||||
github.com/bogdanfinn/fhttp v0.6.8/go.mod h1:A+EKDzMx2hb4IUbMx4TlkoHnaJEiLl8r/1Ss1Y+5e5M=
|
||||
github.com/bogdanfinn/quic-go-utls v1.0.9-utls h1:tV6eDEiRbRCcepALSzxR94JUVD3N3ACIiRLgyc2Ep8s=
|
||||
github.com/bogdanfinn/quic-go-utls v1.0.9-utls/go.mod h1:aHph9B9H9yPOt5xnhWKSOum27DJAqpiHzwX+gjvaXcg=
|
||||
github.com/bogdanfinn/tls-client v1.15.1 h1:KiFAlED55DJ8Fcocn+/1nX6PrDFcttIHAf/GDkV6KN8=
|
||||
github.com/bogdanfinn/tls-client v1.15.1/go.mod h1:LsU6mXVn8MOFDwTkyRfI7V1BZM1p0wf2ZfZsICW/1fM=
|
||||
github.com/bogdanfinn/utls v1.7.7-barnius h1:OuJ497cc7F3yKNVHRsYPQdGggmk5x6+V5ZlrCR7fOLU=
|
||||
github.com/bogdanfinn/utls v1.7.7-barnius/go.mod h1:aAK1VZQlpKZClF1WEQeq6kyclbkPq4hz6xTbB5xSlmg=
|
||||
github.com/bogdanfinn/websocket v1.5.5-barnius h1:bY+qnxpai1qe7Jmjx+Sds/cmOSpuuLoR8x61rWltjOI=
|
||||
github.com/bogdanfinn/websocket v1.5.5-barnius/go.mod h1:gvvEw6pTKHb7yOiFvIfAFTStQWyrm25BMVCTj5wRSsI=
|
||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
|
|
@ -60,6 +74,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/randall77/makefat v0.0.0-20260406194835-1b91746796b7 h1:m1yKMZwDSXkT5o2MKhd6ihdzb2dYb6eElNE04xjOSEY=
|
||||
github.com/randall77/makefat v0.0.0-20260406194835-1b91746796b7/go.mod h1:T1TLSfyWVBRXVGzWd0o9BI4kfoO9InEgfQe4NV3mLz8=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
|
|
@ -74,6 +90,8 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
|||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5 h1:YqAladjX7xpA6BM04leXMWAEjS0mTZ5kUU9KRBriQJc=
|
||||
github.com/tam7t/hpkp v0.0.0-20160821193359-2b70b4024ed5/go.mod h1:2JjD2zLQYH5HO74y5+aE3remJQvl6q4Sn6aWA2wD1Ng=
|
||||
github.com/twmb/murmur3 v1.1.8 h1:8Yt9taO/WN3l08xErzjeschgZU2QSrwm1kclYq+0aRg=
|
||||
github.com/twmb/murmur3 v1.1.8/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq3OSQ=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
|
|
@ -85,6 +103,8 @@ github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPc
|
|||
github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.5.2 h1:LbtPTcP8A5k9WPXj54PPPbjcI4Y6lhyOZXn+VS7wNko=
|
||||
go.uber.org/mock v0.5.2/go.mod h1:wLlUxC2vVTPTaE3UD51E0BGOAElKrILxhVSDYQLld5o=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f h1:ziUVAjmTPwQMBmYR1tbdRFJPtTcQUI12fH9QQjfb0Sw=
|
||||
go4.org v0.0.0-20260112195520-a5071408f32f/go.mod h1:ZRJnO5ZI4zAwMFp+dS1+V6J6MSyAowhRqAE+DPa1Xp0=
|
||||
golang.design/x/clipboard v0.8.0 h1:6VEcH28wwcSgKc+vnxHHDWiRjrakTQIAJnPUrt3aOgg=
|
||||
|
|
@ -93,6 +113,8 @@ golang.design/x/x11 v0.2.0 h1:Uiwu2guGihsJX/ZCzpoDPFz5gR/Qntm08mvoBCmRydo=
|
|||
golang.design/x/x11 v0.2.0/go.mod h1:/5q1mFkdc1rL8mvB7DsQFi6as4tIkBv4FXjcP07mrkE=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/exp/shiny v0.0.0-20260727155853-b88d891fe743 h1:rQLtGtwpYzQUyxCqrT3i4lGgvC1Q4G72JTHYncg6AvY=
|
||||
golang.org/x/exp/shiny v0.0.0-20260727155853-b88d891fe743/go.mod h1:JRiBNgSZB8uVKtHGrvbGDQ8p9Ghn3+lkFSk1KqRIiPc=
|
||||
golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I=
|
||||
|
|
@ -103,13 +125,17 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91
|
|||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211104170005-ce137452f963/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -123,6 +149,7 @@ golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
|||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
|
|
|
|||
18
internal/gateway/dialer.go
Normal file
18
internal/gateway/dialer.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//go:build !no_spoof_tls_fingerprint
|
||||
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"github.com/ayn2op/discordo/internal/tls"
|
||||
tls_client "github.com/bogdanfinn/tls-client"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func NewDialer() websocket.Dialer {
|
||||
client := tls.NewClient(tls_client.WithForceHttp1())
|
||||
dialer := *websocket.DefaultDialer
|
||||
dialer.Proxy = nil
|
||||
dialer.EnableCompression = true
|
||||
dialer.NetDialTLSContext = client.GetTLSDialer()
|
||||
return dialer
|
||||
}
|
||||
11
internal/gateway/dialer_standard.go
Normal file
11
internal/gateway/dialer_standard.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
//go:build no_spoof_tls_fingerprint
|
||||
|
||||
package gateway
|
||||
|
||||
import "github.com/gorilla/websocket"
|
||||
|
||||
func NewDialer() websocket.Dialer {
|
||||
dialer := *websocket.DefaultDialer
|
||||
dialer.EnableCompression = true
|
||||
return dialer
|
||||
}
|
||||
18
internal/gateway/dialer_standard_test.go
Normal file
18
internal/gateway/dialer_standard_test.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//go:build no_spoof_tls_fingerprint
|
||||
|
||||
package gateway
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNewDialer(t *testing.T) {
|
||||
dialer := NewDialer()
|
||||
if dialer.NetDialTLSContext != nil {
|
||||
t.Error("custom TLS dialer is enabled")
|
||||
}
|
||||
if dialer.Proxy == nil {
|
||||
t.Error("environment proxy is disabled")
|
||||
}
|
||||
if !dialer.EnableCompression {
|
||||
t.Error("compression is disabled")
|
||||
}
|
||||
}
|
||||
25
internal/gateway/dialer_test.go
Normal file
25
internal/gateway/dialer_test.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
//go:build !no_spoof_tls_fingerprint
|
||||
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func TestNewDialer(t *testing.T) {
|
||||
dialer := NewDialer()
|
||||
if dialer.NetDialTLSContext == nil {
|
||||
t.Error("TLS dialer is nil")
|
||||
}
|
||||
if dialer.Proxy != nil {
|
||||
t.Error("proxy is enabled")
|
||||
}
|
||||
if !dialer.EnableCompression {
|
||||
t.Error("compression is disabled")
|
||||
}
|
||||
if websocket.DefaultDialer.NetDialTLSContext != nil {
|
||||
t.Error("default dialer was modified")
|
||||
}
|
||||
}
|
||||
20
internal/gateway/gateway.go
Normal file
20
internal/gateway/gateway.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package gateway
|
||||
|
||||
import (
|
||||
"github.com/ayn2op/arikawa/v3/api"
|
||||
discordgateway "github.com/ayn2op/arikawa/v3/gateway"
|
||||
"github.com/ayn2op/arikawa/v3/utils/ws"
|
||||
"github.com/ayn2op/discordo/internal/http"
|
||||
)
|
||||
|
||||
const gatewayURL = "wss://gateway.discord.gg"
|
||||
|
||||
func New(id discordgateway.Identifier) *discordgateway.Gateway {
|
||||
codec := ws.NewCodec(discordgateway.OpUnmarshalers)
|
||||
codec.Headers.Set("Origin", api.BaseEndpoint)
|
||||
codec.Headers.Set("User-Agent", http.BrowserUserAgent())
|
||||
|
||||
conn := ws.NewConnWithDialer(codec, NewDialer())
|
||||
socket := ws.NewCustomWebsocket(conn, discordgateway.AddGatewayParams(gatewayURL))
|
||||
return discordgateway.FromWebsocketGateway(ws.NewGateway(socket, nil), discordgateway.State{Identifier: id})
|
||||
}
|
||||
|
|
@ -1,18 +1,95 @@
|
|||
//go:build !no_spoof_tls_fingerprint
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"maps"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/ayn2op/arikawa/v3/api"
|
||||
"github.com/ayn2op/arikawa/v3/utils/httputil"
|
||||
"github.com/ayn2op/arikawa/v3/utils/httputil/httpdriver"
|
||||
"github.com/ayn2op/discordo/internal/tls"
|
||||
fhttp "github.com/bogdanfinn/fhttp"
|
||||
tls_client "github.com/bogdanfinn/tls-client"
|
||||
)
|
||||
|
||||
func NewClient(token string) *api.Client {
|
||||
stdClient := new(http.Client)
|
||||
stdClient.Transport = NewTransport()
|
||||
httpClient := httputil.NewClientWithDriver(httpdriver.WrapClient(*stdClient))
|
||||
apiClient := api.NewCustomClient(token, httpClient)
|
||||
apiClient.UserAgent = BrowserUserAgent
|
||||
return apiClient
|
||||
httpClient := httputil.NewClientWithDriver(wrapClient(tls.NewClient()))
|
||||
client := api.NewCustomClient(token, httpClient)
|
||||
client.UserAgent = BrowserUserAgent()
|
||||
return client
|
||||
}
|
||||
|
||||
type client struct {
|
||||
tls_client.HttpClient
|
||||
}
|
||||
|
||||
var _ httpdriver.Client = (*client)(nil)
|
||||
|
||||
func wrapClient(c tls_client.HttpClient) httpdriver.Client {
|
||||
return &client{HttpClient: c}
|
||||
}
|
||||
|
||||
func (c *client) NewRequest(ctx context.Context, method, url string) (httpdriver.Request, error) {
|
||||
req, err := fhttp.NewRequestWithContext(ctx, method, url, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*request)(req), nil
|
||||
}
|
||||
|
||||
func (c *client) Do(req httpdriver.Request) (httpdriver.Response, error) {
|
||||
resp, err := c.HttpClient.Do((*fhttp.Request)(req.(*request)))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*response)(resp), nil
|
||||
}
|
||||
|
||||
type request fhttp.Request
|
||||
|
||||
var _ httpdriver.Request = (*request)(nil)
|
||||
|
||||
func (r *request) GetPath() string {
|
||||
return r.URL.Path
|
||||
}
|
||||
|
||||
func (r *request) GetContext() context.Context {
|
||||
return (*fhttp.Request)(r).Context()
|
||||
}
|
||||
|
||||
func (r *request) AddHeader(header http.Header) {
|
||||
maps.Copy(r.Header, fhttp.Header(header))
|
||||
}
|
||||
|
||||
func (r *request) AddQuery(values url.Values) {
|
||||
query := r.URL.Query()
|
||||
for key, value := range values {
|
||||
query[key] = append(query[key], value...)
|
||||
}
|
||||
r.URL.RawQuery = query.Encode()
|
||||
}
|
||||
|
||||
func (r *request) WithBody(body io.ReadCloser) {
|
||||
r.Body = body
|
||||
}
|
||||
|
||||
type response fhttp.Response
|
||||
|
||||
var _ httpdriver.Response = (*response)(nil)
|
||||
|
||||
func (r *response) GetStatus() int {
|
||||
return r.StatusCode
|
||||
}
|
||||
|
||||
func (r *response) GetHeader() http.Header {
|
||||
return http.Header(r.Header)
|
||||
}
|
||||
|
||||
func (r *response) GetBody() io.ReadCloser {
|
||||
return r.Body
|
||||
}
|
||||
|
|
|
|||
19
internal/http/client_standard.go
Normal file
19
internal/http/client_standard.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
//go:build no_spoof_tls_fingerprint
|
||||
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/ayn2op/arikawa/v3/api"
|
||||
"github.com/ayn2op/arikawa/v3/utils/httputil"
|
||||
"github.com/ayn2op/arikawa/v3/utils/httputil/httpdriver"
|
||||
)
|
||||
|
||||
func NewClient(token string) *api.Client {
|
||||
stdClient := http.Client{Transport: NewTransport()}
|
||||
httpClient := httputil.NewClientWithDriver(httpdriver.WrapClient(stdClient))
|
||||
client := api.NewCustomClient(token, httpClient)
|
||||
client.UserAgent = BrowserUserAgent()
|
||||
return client
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package http
|
|||
import (
|
||||
"github.com/ayn2op/arikawa/v3/discord"
|
||||
"github.com/ayn2op/arikawa/v3/gateway"
|
||||
"github.com/ayn2op/discordo/internal/tls"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
|
|
@ -11,14 +12,13 @@ import (
|
|||
const (
|
||||
OS = "Windows"
|
||||
OSVersion = "10"
|
||||
|
||||
Browser = "Chrome"
|
||||
BrowserVersion = "143.0.0.0"
|
||||
BrowserUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + Browser + "/" + BrowserVersion + " Safari/537.36"
|
||||
|
||||
Locale = discord.EnglishUS
|
||||
Locale = discord.EnglishUS
|
||||
)
|
||||
|
||||
func BrowserUserAgent() string {
|
||||
return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) " + tls.BrowserName() + "/" + tls.BrowserVersion() + " Safari/537.36"
|
||||
}
|
||||
|
||||
func CommonProperties() gateway.IdentifyProperties {
|
||||
return gateway.IdentifyProperties{
|
||||
gateway.IdentifyDevice: "",
|
||||
|
|
@ -26,9 +26,9 @@ func CommonProperties() gateway.IdentifyProperties {
|
|||
gateway.IdentifyOS: OS,
|
||||
"os_version": OSVersion,
|
||||
|
||||
gateway.IdentifyBrowser: Browser,
|
||||
"browser_version": BrowserVersion,
|
||||
"browser_user_agent": BrowserUserAgent,
|
||||
gateway.IdentifyBrowser: tls.BrowserName(),
|
||||
"browser_version": tls.BrowserVersion(),
|
||||
"browser_user_agent": BrowserUserAgent(),
|
||||
|
||||
"client_build_number": ClientBuildNumber,
|
||||
"client_event_source": nil,
|
||||
|
|
|
|||
35
internal/http/properties_test.go
Normal file
35
internal/http/properties_test.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ayn2op/arikawa/v3/gateway"
|
||||
"github.com/ayn2op/discordo/internal/tls"
|
||||
)
|
||||
|
||||
func TestBrowserIdentity(t *testing.T) {
|
||||
properties := CommonProperties()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
got any
|
||||
want any
|
||||
}{
|
||||
{"properties browser", properties[gateway.IdentifyBrowser], tls.BrowserName()},
|
||||
{"properties version", properties["browser_version"], tls.BrowserVersion()},
|
||||
{"properties user agent", properties["browser_user_agent"], BrowserUserAgent()},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if test.got != test.want {
|
||||
t.Fatalf("got = %v, want = %v", test.got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if product := tls.BrowserName() + "/" + tls.BrowserVersion(); !strings.Contains(BrowserUserAgent(), product) {
|
||||
t.Fatalf("user agent does not contain %q", product)
|
||||
}
|
||||
}
|
||||
18
internal/tls/client.go
Normal file
18
internal/tls/client.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
//go:build !no_spoof_tls_fingerprint
|
||||
|
||||
package tls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
tls_client "github.com/bogdanfinn/tls-client"
|
||||
)
|
||||
|
||||
func NewClient(options ...tls_client.HttpClientOption) tls_client.HttpClient {
|
||||
options = append([]tls_client.HttpClientOption{tls_client.WithClientProfile(clientProfile)}, options...)
|
||||
client, err := tls_client.NewHttpClient(tls_client.NewNoopLogger(), options...)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to create new tls http client: %w", err))
|
||||
}
|
||||
return client
|
||||
}
|
||||
15
internal/tls/client_test.go
Normal file
15
internal/tls/client_test.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
//go:build !no_spoof_tls_fingerprint
|
||||
|
||||
package tls
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestNewClient(t *testing.T) {
|
||||
client := NewClient()
|
||||
if client == nil {
|
||||
t.Fatal("client is nil")
|
||||
}
|
||||
if client.GetTLSDialer() == nil {
|
||||
t.Fatal("TLS dialer is nil")
|
||||
}
|
||||
}
|
||||
13
internal/tls/profile.go
Normal file
13
internal/tls/profile.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package tls
|
||||
|
||||
import "github.com/bogdanfinn/tls-client/profiles"
|
||||
|
||||
var clientProfile = profiles.Chrome_146
|
||||
|
||||
func BrowserName() string {
|
||||
return clientProfile.GetClientHelloId().Client
|
||||
}
|
||||
|
||||
func BrowserVersion() string {
|
||||
return clientProfile.GetClientHelloId().Version + ".0.0.0"
|
||||
}
|
||||
28
internal/tls/profile_test.go
Normal file
28
internal/tls/profile_test.go
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package tls
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/bogdanfinn/tls-client/profiles"
|
||||
)
|
||||
|
||||
func TestBrowserIdentity(t *testing.T) {
|
||||
hello := profiles.Chrome_146.GetClientHelloId()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
got string
|
||||
want string
|
||||
}{
|
||||
{"name", BrowserName(), hello.Client},
|
||||
{"version", BrowserVersion(), hello.Version + ".0.0.0"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if test.got != test.want {
|
||||
t.Fatalf("got = %q, want = %q", test.got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/ayn2op/arikawa/v3/utils/httputil"
|
||||
"github.com/ayn2op/arikawa/v3/utils/ws"
|
||||
"github.com/ayn2op/discordo/internal/config"
|
||||
clientgateway "github.com/ayn2op/discordo/internal/gateway"
|
||||
"github.com/ayn2op/discordo/internal/http"
|
||||
"github.com/ayn2op/discordo/internal/ui"
|
||||
"github.com/ayn2op/ningen/v3"
|
||||
|
|
@ -85,7 +86,8 @@ func NewModel(app *tview.Application, cfg *config.Config, token string) *Model {
|
|||
Properties: http.IdentifyProperties(),
|
||||
})
|
||||
|
||||
session := session.NewCustom(id, http.NewClient(token), handler.New())
|
||||
session := session.NewWithGateway(clientgateway.New(id), handler.New())
|
||||
session.Client = http.NewClient(token)
|
||||
state := state.NewFromSession(session, defaultstore.New())
|
||||
m.state = ningen.FromState(state)
|
||||
m.state.DontWaitForReady = true
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ayn2op/arikawa/v3/utils/httputil"
|
||||
"github.com/ayn2op/discordo/internal/gateway"
|
||||
"github.com/ayn2op/discordo/internal/http"
|
||||
"github.com/ayn2op/tview"
|
||||
"github.com/gorilla/websocket"
|
||||
|
|
@ -39,8 +40,9 @@ type connCloseMsg struct{}
|
|||
func (m *Model) connect() tview.Cmd {
|
||||
return func() tview.Msg {
|
||||
headers := http.Headers()
|
||||
headers.Set("User-Agent", http.BrowserUserAgent)
|
||||
conn, _, err := websocket.DefaultDialer.Dial(remoteAuthGatewayURL, headers)
|
||||
headers.Set("User-Agent", http.BrowserUserAgent())
|
||||
dialer := gateway.NewDialer()
|
||||
conn, _, err := dialer.Dial(remoteAuthGatewayURL, headers)
|
||||
if err != nil {
|
||||
return newErrMsg(err)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue