improve: reality 处理使用脚本重定向的情况

This commit is contained in:
Mmx233
2024-01-01 16:56:33 +08:00
parent 3a16b9feae
commit 5e73e952bf

View File

@@ -10,8 +10,10 @@ import (
"math/rand" "math/rand"
"net/http" "net/http"
"net/url" "net/url"
"regexp"
"strings" "strings"
"time" "time"
"unsafe"
) )
type Api struct { type Api struct {
@@ -93,7 +95,26 @@ func (a *Api) GetUserInfo() (map[string]interface{}, error) {
return a.request("cgi-bin/rad_user_info", nil) return a.request("cgi-bin/rad_user_info", nil)
} }
func (a *Api) FollowRedirect(addr *url.URL, onNext func(addr *url.URL) error) (*url.URL, error) { func (a *Api) _JoinRedirectLocation(addr *url.URL, loc string) (*url.URL, error) {
if loc == "" {
return nil, errors.New("目标跳转地址缺失")
}
if strings.HasPrefix(loc, "/") {
addr.Path = strings.TrimPrefix(loc, "/")
return addr, nil
} else {
return url.Parse(loc)
}
}
type _FollowRedirectConfig struct {
// 覆盖响应处理逻辑,设置后 onNextAddr 无效
onResponse func(res *http.Response) (next *url.URL, err error)
// 获取到下一个请求地址时触发
onNextAddr func(addr *url.URL) error
}
func (a *Api) _FollowRedirect(addr *url.URL, conf _FollowRedirectConfig) (*url.URL, error) {
addrCopy := *addr addrCopy := *addr
addr = &addrCopy addr = &addrCopy
for { for {
@@ -109,26 +130,31 @@ func (a *Api) FollowRedirect(addr *url.URL, onNext func(addr *url.URL) error) (*
if err != nil { if err != nil {
return nil, err return nil, err
} }
if conf.onResponse != nil {
var nextAddr *url.URL
nextAddr, err = conf.onResponse(res)
if err != nil {
return nil, err
} else if nextAddr == nil {
break
}
addr = nextAddr
continue
}
_, _ = io.Copy(io.Discard, res.Body) _, _ = io.Copy(io.Discard, res.Body)
_ = res.Body.Close() _ = res.Body.Close()
loc := res.Header.Get("location")
if res.StatusCode < 300 { if res.StatusCode < 300 {
break break
} else if res.StatusCode < 400 { } else if res.StatusCode < 400 {
if loc == "" { addr, err = a._JoinRedirectLocation(addr, res.Header.Get("location"))
return nil, errors.New("目标跳转地址缺失")
}
if strings.HasPrefix(loc, "/") {
addr.Path = strings.TrimPrefix(loc, "/")
} else {
addr, err = url.Parse(loc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
} if conf.onNextAddr != nil {
if err = onNext(addr); err != nil { if err = conf.onNextAddr(addr); err != nil {
return nil, err return nil, err
} }
}
} else { } else {
return nil, fmt.Errorf("server return http status %d", res.StatusCode) return nil, fmt.Errorf("server return http status %d", res.StatusCode)
} }
@@ -136,7 +162,7 @@ func (a *Api) FollowRedirect(addr *url.URL, onNext func(addr *url.URL) error) (*
return addr, nil return addr, nil
} }
func (a *Api) searchAcid(query url.Values) (string, bool) { func (a *Api) _SearchAcid(query url.Values) (string, bool) {
addr := query.Get(`ac_id`) addr := query.Get(`ac_id`)
return addr, addr != "" return addr, addr != ""
} }
@@ -150,13 +176,15 @@ func (a *Api) DetectAcid() (string, error) {
var AcidFound = errors.New("acid found") var AcidFound = errors.New("acid found")
var acid string var acid string
_, err = a.FollowRedirect(baseUrl, func(addr *url.URL) error { _, err = a._FollowRedirect(baseUrl, _FollowRedirectConfig{
onNextAddr: func(addr *url.URL) error {
var ok bool var ok bool
acid, ok = a.searchAcid(addr.Query()) acid, ok = a._SearchAcid(addr.Query())
if ok { if ok {
return AcidFound return AcidFound
} }
return nil return nil
},
}) })
if err != nil { if err != nil {
if errors.Is(err, AcidFound) { if errors.Is(err, AcidFound) {
@@ -175,15 +203,36 @@ func (a *Api) Reality(addr string, getAcid bool) (acid string, online bool, err
} }
var AlreadyOnline = errors.New("already online") var AlreadyOnline = errors.New("already online")
var finalUrl *url.URL var finalUrl *url.URL
finalUrl, err = a.FollowRedirect(startUrl, func(addr *url.URL) error { finalUrl, err = a._FollowRedirect(startUrl, _FollowRedirectConfig{
// 任一跳转没有跳出初始域名说明已经在线 onResponse: func(res *http.Response) (next *url.URL, err error) {
if addr.Host == startUrl.Host { defer res.Body.Close()
return AlreadyOnline if res.StatusCode < 300 {
var body []byte
body, err = io.ReadAll(res.Body)
if err != nil {
return
} }
if getAcid {
acid, _ = a.searchAcid(addr.Query()) var reg *regexp.Regexp
reg, err = regexp.Compile(`<script>top\.self\.location\.href='(.*)'</script>`)
if err != nil {
return
} }
return nil
result := reg.FindSubmatch(body)
if len(result) == 2 {
nextBytes := result[1]
nextAddr := unsafe.String(unsafe.SliceData(nextBytes), len(nextBytes))
next, err = url.Parse(nextAddr)
}
} else if res.StatusCode < 400 {
next, err = a._JoinRedirectLocation(res.Request.URL, res.Header.Get("location"))
}
if getAcid && next != nil {
acid, _ = a._SearchAcid(next.Query())
}
return
},
}) })
if err != nil { if err != nil {
if errors.Is(err, AlreadyOnline) { if errors.Is(err, AlreadyOnline) {