diff --git a/api/playback/allanime_client.go b/api/playback/allanime_client.go index 8eee274..2ea08c9 100644 --- a/api/playback/allanime_client.go +++ b/api/playback/allanime_client.go @@ -1,7 +1,6 @@ package playback import ( - "bufio" "bytes" "context" "crypto/aes" @@ -12,7 +11,7 @@ import ( "fmt" "io" "log" - "net" + "mal/pkg/net/utls" "net/http" "net/url" "os" @@ -20,9 +19,6 @@ import ( "strconv" "strings" "time" - - utls "github.com/refraction-networking/utls" - "golang.org/x/net/http2" ) const ( @@ -50,54 +46,8 @@ var ( } ) -// utlsRoundTripper uses uTLS + HTTP/2 to mimic Firefox and bypass Cloudflare JA3 detection -type utlsRoundTripper struct{} - -func (rt *utlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - dialer := &net.Dialer{Timeout: 10 * time.Second} - host := req.URL.Hostname() - port := req.URL.Port() - if port == "" { - port = "443" - } - addr := host + ":" + port - - rawConn, err := dialer.DialContext(req.Context(), "tcp", addr) - if err != nil { - return nil, fmt.Errorf("tcp dial: %w", err) - } - - uconn := utls.UClient(rawConn, &utls.Config{ - ServerName: host, - NextProtos: []string{"h2", "http/1.1"}, - }, utls.HelloFirefox_120) - - if err := uconn.HandshakeContext(req.Context()); err != nil { - uconn.Close() - return nil, fmt.Errorf("utls handshake: %w", err) - } - - alpn := uconn.ConnectionState().NegotiatedProtocol - if alpn == "h2" { - t := &http2.Transport{} - cc, err := t.NewClientConn(uconn) - if err != nil { - uconn.Close() - return nil, fmt.Errorf("http2 client conn: %w", err) - } - return cc.RoundTrip(req) - } - - // Fallback to HTTP/1.1 - if err := req.Write(uconn); err != nil { - uconn.Close() - return nil, fmt.Errorf("http1 write: %w", err) - } - return http.ReadResponse(bufio.NewReader(uconn), req) -} - var allAnimeUTLSClient = &http.Client{ - Transport: &utlsRoundTripper{}, + Transport: &utls.UtlsRoundTripper{}, Timeout: 30 * time.Second, } diff --git a/pkg/net/utls/utls.go b/pkg/net/utls/utls.go new file mode 100644 index 0000000..c3903e4 --- /dev/null +++ b/pkg/net/utls/utls.go @@ -0,0 +1,62 @@ +package utls + +import ( + "bufio" + "fmt" + "net" + "net/http" + "time" + + utls "github.com/refraction-networking/utls" + "golang.org/x/net/http2" +) + +// UtlsRoundTripper uses uTLS + HTTP/2 to mimic Firefox and bypass Cloudflare JA3 detection +type UtlsRoundTripper struct{} + +func (rt *UtlsRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + dialer := &net.Dialer{Timeout: 10 * time.Second} + host := req.URL.Hostname() + port := req.URL.Port() + if port == "" { + if req.URL.Scheme == "https" { + port = "443" + } else { + port = "80" + } + } + addr := net.JoinHostPort(host, port) + + rawConn, err := dialer.DialContext(req.Context(), "tcp", addr) + if err != nil { + return nil, fmt.Errorf("tcp dial: %w", err) + } + + uconn := utls.UClient(rawConn, &utls.Config{ + ServerName: host, + NextProtos: []string{"h2", "http/1.1"}, + }, utls.HelloFirefox_120) + + if err := uconn.HandshakeContext(req.Context()); err != nil { + uconn.Close() + return nil, fmt.Errorf("utls handshake: %w", err) + } + + alpn := uconn.ConnectionState().NegotiatedProtocol + if alpn == "h2" { + t := &http2.Transport{} + cc, err := t.NewClientConn(uconn) + if err != nil { + uconn.Close() + return nil, fmt.Errorf("http2 client conn: %w", err) + } + return cc.RoundTrip(req) + } + + // Fallback to HTTP/1.1 + if err := req.Write(uconn); err != nil { + uconn.Close() + return nil, fmt.Errorf("http1 write: %w", err) + } + return http.ReadResponse(bufio.NewReader(uconn), req) +}