Skip to content

Commit d0cec67

Browse files
authored
fix(lanzou): handle acw_sc__v2 anti-crawler challenge on all requests (#9548)
The acw_sc__v2 challenge can be served on any pan.lanzoui.com request (share page, iframe page, ajaxm.php), but it was only handled for the first share page. This caused intermittent failures: - "uid variable not find" when mydisk.php returned the challenge on init - "not find data" when the iframe download page returned the challenge - empty "failed get link" when ajaxm.php POST returned the challenge (the challenge HTML was parsed as JSON, zt=0 -> empty info) Also fix HexXor, which was broken since the driver was introduced and made every challenge unsolvable: - bytes.NewBuffer(make([]byte, len(hex1))) prefixed the result with len(hex1) null bytes - missing zero-padding produced a single hex char when xor result < 0x10, misaligning the whole string Move the challenge solve/retry loop down into request() so every GET/POST transparently handles it, and simplify getHtml accordingly.
1 parent 0e3006c commit d0cec67

2 files changed

Lines changed: 67 additions & 62 deletions

File tree

drivers/lanzou/help.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package lanzou
22

33
import (
4-
"bytes"
54
"fmt"
65
"net/http"
76
"regexp"
@@ -144,11 +143,13 @@ func Unbox(hex string) string {
144143
}
145144

146145
func HexXor(hex1, hex2 string) string {
147-
out := bytes.NewBuffer(make([]byte, len(hex1)))
148-
for i := 0; i < len(hex1) && i < len(hex2); i += 2 {
146+
var out strings.Builder
147+
out.Grow(len(hex1))
148+
for i := 0; i+2 <= len(hex1) && i+2 <= len(hex2); i += 2 {
149149
v1, _ := strconv.ParseInt(hex1[i:i+2], 16, 64)
150150
v2, _ := strconv.ParseInt(hex2[i:i+2], 16, 64)
151-
out.WriteString(strconv.FormatInt(v1^v2, 16))
151+
// 必须补足前导 0,否则异或结果 < 0x10 时只输出一个字符,导致整个 hex 串错位
152+
fmt.Fprintf(&out, "%02x", v1^v2)
152153
}
153154
return out.String()
154155
}

drivers/lanzou/util.go

Lines changed: 62 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -95,35 +95,55 @@ func (d *LanZou) _post(url string, callback base.ReqCallback, resp interface{},
9595
}
9696

9797
func (d *LanZou) request(url string, method string, callback base.ReqCallback, up bool) ([]byte, error) {
98-
var req *resty.Request
98+
var client *resty.Client
9999
if up {
100100
once.Do(func() {
101101
upClient = base.NewRestyClient().SetTimeout(120 * time.Second)
102102
})
103-
req = upClient.R()
103+
client = upClient
104104
} else {
105-
req = base.RestyClient.R()
105+
client = base.RestyClient
106106
}
107107

108-
req.SetHeaders(map[string]string{
109-
"Referer": "https://pc.woozooo.com",
110-
"User-Agent": d.UserAgent,
111-
})
112-
113-
if d.Cookie != "" {
114-
req.SetHeader("cookie", d.Cookie)
115-
}
108+
// acw_sc__v2 反爬挑战可能出现在任意页面/接口(分享页、iframe 页、ajaxm.php 等)。
109+
// 挑战页内嵌 arg1,需据此算出 cookie 后用同一请求重试,故在最底层统一处理。
110+
var acwScV2 string
111+
var body []byte
112+
for i := 0; i < 3; i++ {
113+
req := client.R()
114+
req.SetHeaders(map[string]string{
115+
"Referer": "https://pc.woozooo.com",
116+
"User-Agent": d.UserAgent,
117+
})
118+
if d.Cookie != "" {
119+
req.SetHeader("cookie", d.Cookie)
120+
}
121+
if acwScV2 != "" {
122+
req.SetCookie(&http.Cookie{Name: "acw_sc__v2", Value: acwScV2})
123+
}
124+
if callback != nil {
125+
callback(req)
126+
}
116127

117-
if callback != nil {
118-
callback(req)
119-
}
128+
res, err := req.Execute(method, url)
129+
if err != nil {
130+
return nil, err
131+
}
132+
body = res.Body()
133+
log.Debugf("lanzou request: url=>%s ,stats=>%d ,body => %s\n", res.Request.URL, res.StatusCode(), res.String())
120134

121-
res, err := req.Execute(method, url)
122-
if err != nil {
123-
return nil, err
135+
if findAcwScV2Reg.Match(body) {
136+
vs, e := CalcAcwScV2(string(body))
137+
if e != nil {
138+
log.Errorf("lanzou: err => acw_sc__v2 validation error ,data => %s\n", body)
139+
return body, e
140+
}
141+
acwScV2 = vs
142+
continue
143+
}
144+
return body, nil
124145
}
125-
log.Debugf("lanzou request: url=>%s ,stats=>%d ,body => %s\n", res.Request.URL, res.StatusCode(), res.String())
126-
return res.Body(), err
146+
return body, errors.New("acw_sc__v2 validation error")
127147
}
128148

129149
func (d *LanZou) Login() ([]*http.Cookie, error) {
@@ -267,42 +287,28 @@ var findDownPageParamReg = regexp.MustCompile(`<iframe.*?src="(.+?)"`)
267287
// 获取文件ID
268288
var findFileIDReg = regexp.MustCompile(`'/ajaxm\.php\?file=(\d+)'`)
269289

290+
// GET 页面并去除注释(acw_sc__v2 反爬挑战已在 request 层统一处理)
291+
func (d *LanZou) getHtml(url string, callback base.ReqCallback) (string, error) {
292+
data, err := d.get(url, callback)
293+
if err != nil {
294+
return "", err
295+
}
296+
return RemoveNotes(string(data)), nil
297+
}
298+
270299
// 获取分享链接主界面
271300
func (d *LanZou) getShareUrlHtml(shareID string) (string, error) {
272-
var vs string
273-
for i := 0; i < 3; i++ {
274-
firstPageData, err := d.get(fmt.Sprint(d.ShareUrl, "/", shareID),
275-
func(req *resty.Request) {
276-
if vs != "" {
277-
req.SetCookie(&http.Cookie{
278-
Name: "acw_sc__v2",
279-
Value: vs,
280-
})
281-
}
282-
})
283-
if err != nil {
284-
return "", err
285-
}
286-
287-
firstPageDataStr := RemoveNotes(string(firstPageData))
288-
if strings.Contains(firstPageDataStr, "取消分享") {
289-
return "", ErrFileShareCancel
290-
}
291-
if strings.Contains(firstPageDataStr, "文件不存在") {
292-
return "", ErrFileNotExist
293-
}
294-
295-
// acw_sc__v2
296-
if strings.Contains(firstPageDataStr, "acw_sc__v2") {
297-
if vs, err = CalcAcwScV2(firstPageDataStr); err != nil {
298-
log.Errorf("lanzou: err => acw_sc__v2 validation error ,data => %s\n", firstPageDataStr)
299-
return "", err
300-
}
301-
continue
302-
}
303-
return firstPageDataStr, nil
301+
htmlStr, err := d.getHtml(fmt.Sprint(d.ShareUrl, "/", shareID), nil)
302+
if err != nil {
303+
return "", err
304+
}
305+
if strings.Contains(htmlStr, "取消分享") {
306+
return "", ErrFileShareCancel
307+
}
308+
if strings.Contains(htmlStr, "文件不存在") {
309+
return "", ErrFileNotExist
304310
}
305-
return "", errors.New("acw_sc__v2 validation error")
311+
return htmlStr, nil
306312
}
307313

308314
// 通过分享链接获取文件或文件夹
@@ -386,11 +392,10 @@ func (d *LanZou) getFilesByShareUrl(shareID, pwd string, sharePageData string) (
386392
log.Errorf("lanzou: err => not find file page param ,data => %s\n", sharePageData)
387393
return nil, fmt.Errorf("not find file page param")
388394
}
389-
data, err := d.get(fmt.Sprint(d.ShareUrl, urlpaths[1]), nil)
395+
nextPageData, err := d.getHtml(fmt.Sprint(d.ShareUrl, urlpaths[1]), nil)
390396
if err != nil {
391397
return nil, err
392398
}
393-
nextPageData := RemoveNotes(string(data))
394399
param, err = htmlJsonToMap(nextPageData)
395400
if err != nil {
396401
return nil, err
@@ -542,8 +547,8 @@ func (d *LanZou) getFileRealInfo(downURL string) (*int64, *time.Time) {
542547
}
543548

544549
func (d *LanZou) getVeiAndUid() (vei string, uid string, err error) {
545-
var resp []byte
546-
resp, err = d.get("https://pc.woozooo.com/mydisk.php", func(req *resty.Request) {
550+
// mydisk.php 同样可能返回 acw_sc__v2 反爬挑战页,需经 getHtml 处理后再解析
551+
html, err := d.getHtml("https://pc.woozooo.com/mydisk.php", func(req *resty.Request) {
547552
req.SetQueryParams(map[string]string{
548553
"item": "files",
549554
"action": "index",
@@ -553,15 +558,14 @@ func (d *LanZou) getVeiAndUid() (vei string, uid string, err error) {
553558
return
554559
}
555560
// uid
556-
uids := regexp.MustCompile(`uid=([^'"&;]+)`).FindStringSubmatch(string(resp))
561+
uids := regexp.MustCompile(`uid=([^'"&;]+)`).FindStringSubmatch(html)
557562
if len(uids) < 2 {
558563
err = fmt.Errorf("uid variable not find")
559564
return
560565
}
561566
uid = uids[1]
562567

563568
// vei
564-
html := RemoveNotes(string(resp))
565569
data, err := htmlJsonToMap(html)
566570
if err != nil {
567571
return

0 commit comments

Comments
 (0)