mirror of
https://gitgud.io/yats/libkiwi.git
synced 2026-04-30 01:32:04 -04:00
Cerberus support
This commit is contained in:
8
go.mod
8
go.mod
@@ -1,12 +1,12 @@
|
|||||||
module github.com/y-a-t-s/libkiwi
|
module gitgud.io/yats/libkiwi
|
||||||
|
|
||||||
go 1.25.6
|
go 1.25.6
|
||||||
|
|
||||||
require github.com/y-a-t-s/firebird v0.0.0-20251115152558-29bf6ad7c90b
|
require github.com/y-a-t-s/cerberus v0.0.0-20260209172955-b5a3082a97ee
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.3 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||||
golang.org/x/net v0.28.0 // indirect
|
golang.org/x/net v0.49.0 // indirect
|
||||||
golang.org/x/sys v0.23.0 // indirect
|
golang.org/x/sys v0.40.0 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
14
go.sum
14
go.sum
@@ -2,10 +2,12 @@ github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y7
|
|||||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||||
github.com/y-a-t-s/firebird v0.0.0-20251115152558-29bf6ad7c90b h1:tAqjNda25c1SpnoDmDx5b1KIuN0VTgskoU4shwSMdFI=
|
github.com/y-a-t-s/cerberus v0.0.0-20260204195623-34ceeced8f89 h1:mlG5ck3Of8vNKPZX/RXs7T+kJPcsLsK8hcG0kAQScc0=
|
||||||
github.com/y-a-t-s/firebird v0.0.0-20251115152558-29bf6ad7c90b/go.mod h1:/qomDTP1XQRFc0TLzcf0Y5wMHftgl5z40PAb9aTxXGg=
|
github.com/y-a-t-s/cerberus v0.0.0-20260204195623-34ceeced8f89/go.mod h1:CO0/MIkpLBFxuhP1+j7u+IN4JRBSIzk8MNnSIGsmenI=
|
||||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
github.com/y-a-t-s/cerberus v0.0.0-20260209172955-b5a3082a97ee h1:DWnyTaXLd4F3Digp8NVD4v/IOH13VwLc07Jh5eeKxfM=
|
||||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
github.com/y-a-t-s/cerberus v0.0.0-20260209172955-b5a3082a97ee/go.mod h1:CO0/MIkpLBFxuhP1+j7u+IN4JRBSIzk8MNnSIGsmenI=
|
||||||
|
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||||
|
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
|||||||
69
libkiwi.go
69
libkiwi.go
@@ -3,13 +3,14 @@ package libkiwi
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/cookiejar"
|
"net/http/cookiejar"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/y-a-t-s/firebird"
|
"github.com/y-a-t-s/cerberus"
|
||||||
)
|
)
|
||||||
|
|
||||||
type KF struct {
|
type KF struct {
|
||||||
@@ -18,28 +19,27 @@ type KF struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Supply your own http.Client to route through any proxies.
|
// Supply your own http.Client to route through any proxies.
|
||||||
func NewKF(hc http.Client, host string, cookies string) (kf *KF, err error) {
|
func NewKF(hc http.Client, host string, cookies string) (KF, error) {
|
||||||
u, err := parseHost(host)
|
u, err := parseHost(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return KF{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
jar, err := cookiejar.New(nil)
|
if hc.Jar == nil {
|
||||||
if err != nil {
|
jar, err := cookiejar.New(nil)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return KF{}, err
|
||||||
|
}
|
||||||
|
hc.Jar = jar
|
||||||
}
|
}
|
||||||
|
|
||||||
cs, err := parseCookieString(u, cookies)
|
if cookies != "" {
|
||||||
if err != nil {
|
cs, err := parseCookieString(u, cookies)
|
||||||
return nil, err
|
if err != nil {
|
||||||
}
|
return KF{}, err
|
||||||
|
}
|
||||||
|
|
||||||
jar.SetCookies(u, cs)
|
hc.Jar.SetCookies(u, cs)
|
||||||
hc.Jar = jar
|
|
||||||
|
|
||||||
kf = &KF{
|
|
||||||
Client: hc,
|
|
||||||
domain: u,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update host url in case we get redirected across domains.
|
// Update host url in case we get redirected across domains.
|
||||||
@@ -54,7 +54,10 @@ func NewKF(hc http.Client, host string, cookies string) (kf *KF, err error) {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return KF{
|
||||||
|
Client: hc,
|
||||||
|
domain: u,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kf *KF) GetPage(ctx context.Context, u *url.URL) (*http.Response, error) {
|
func (kf *KF) GetPage(ctx context.Context, u *url.URL) (*http.Response, error) {
|
||||||
@@ -76,7 +79,7 @@ func (kf *KF) GetPage(ctx context.Context, u *url.URL) (*http.Response, error) {
|
|||||||
|
|
||||||
// KiwiFlare redirect is signaled by 203 status.
|
// KiwiFlare redirect is signaled by 203 status.
|
||||||
if resp.StatusCode == 203 {
|
if resp.StatusCode == 203 {
|
||||||
err = kf.solveKiwiFlare(ctx)
|
err = kf.SolveKiwiFlare(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -109,22 +112,29 @@ func parseCookieString(u *url.URL, cookies string) ([]*http.Cookie, error) {
|
|||||||
|
|
||||||
func setCookie(jar http.CookieJar, u *url.URL, cookie *http.Cookie) {
|
func setCookie(jar http.CookieJar, u *url.URL, cookie *http.Cookie) {
|
||||||
cookies := jar.Cookies(u)
|
cookies := jar.Cookies(u)
|
||||||
found := false
|
|
||||||
for i, c := range cookies {
|
for i, c := range cookies {
|
||||||
if c.Name == cookie.Name {
|
if c.Name == cookie.Name {
|
||||||
cookies[i] = cookie
|
cookies[i] = cookie
|
||||||
found = true
|
// Store changes and stop looping since it's found.
|
||||||
break
|
jar.SetCookies(u, cookies)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
// Append new cookie if existing one not found.
|
||||||
cookies = append(cookies, cookie)
|
cookies = append(cookies, cookie)
|
||||||
}
|
|
||||||
|
|
||||||
jar.SetCookies(u, cookies)
|
jar.SetCookies(u, cookies)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kf *KF) CookieString() string {
|
||||||
|
var b strings.Builder
|
||||||
|
for _, c := range kf.Client.Jar.Cookies(kf.domain) {
|
||||||
|
fmt.Fprintf(&b, "%s: %s; ", c.Name, c.Value)
|
||||||
|
}
|
||||||
|
return strings.TrimSuffix(b.String(), "; ")
|
||||||
|
}
|
||||||
|
|
||||||
func (kf *KF) RefreshSession(ctx context.Context) (string, error) {
|
func (kf *KF) RefreshSession(ctx context.Context) (string, error) {
|
||||||
// Clear any existing session token to request a new one.
|
// Clear any existing session token to request a new one.
|
||||||
setCookie(kf.Client.Jar, kf.domain, &http.Cookie{
|
setCookie(kf.Client.Jar, kf.domain, &http.Cookie{
|
||||||
@@ -141,19 +151,20 @@ func (kf *KF) RefreshSession(ctx context.Context) (string, error) {
|
|||||||
return regexp.MustCompile(`xf_session=([^;]*)`).FindString(resp.Header.Get("Set-Cookie")), nil
|
return regexp.MustCompile(`xf_session=([^;]*)`).FindString(resp.Header.Get("Set-Cookie")), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (kf *KF) solveKiwiFlare(ctx context.Context) error {
|
func (kf *KF) SolveKiwiFlare(ctx context.Context) error {
|
||||||
c, err := firebird.NewChallenge(kf.Client, kf.domain.String())
|
c, err := cerberus.NewChallenge(ctx, kf.Client, kf.domain.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s, err := firebird.Solve(ctx, c)
|
s, err := cerberus.Solve(ctx, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, err = firebird.Submit(kf.Client, s)
|
resp, err := cerberus.Submit(ctx, kf.Client, s, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
const TEST_HOST = "kiwifarms.net"
|
const TEST_HOST = "kiwifarms.st"
|
||||||
|
|
||||||
func TestGetPage(t *testing.T) {
|
func TestGetPage(t *testing.T) {
|
||||||
cookies := os.Getenv("TEST_COOKIES")
|
cookies := os.Getenv("TEST_COOKIES")
|
||||||
|
|||||||
Reference in New Issue
Block a user