diff --git a/api/AntiGateV2.go b/api/AntiGateV2.go index 210fd2b..2e50913 100644 --- a/api/AntiGateV2.go +++ b/api/AntiGateV2.go @@ -2,6 +2,7 @@ package api import ( "encoding/json" + "fmt" "io" "net/http" "reflect" @@ -78,6 +79,12 @@ var ( ErrorCode: "ERROR_BAD_REQUEST", ErrorDescription: "Bad request", } + + errorProxyConnectRefused = &antigateError{ + ErrorId: 25, + ErrorCode: "ERROR_PROXY_CONNECT_REFUSED", + ErrorDescription: "Bad proxy", + } ) func (a *antigateV2Api) getTaskResult(request struct { @@ -150,6 +157,35 @@ func (a *antigateV2Api) getTaskResult(request struct { return response } +func collectAntiGateV2Proxy(task any) *string { + var proxy struct { + ProxyType string `json:"proxyType"` + ProxyAddress string `json:"proxyAddress"` + ProxyPort uint16 `json:"proxyPort"` + ProxyLogin *string `json:"proxyLogin"` + ProxyPassword *string `json:"proxyPassword"` + } + if err := mapstructure.Decode(task, proxy); err != nil { + return nil + } + + if proxy.ProxyType == "" || proxy.ProxyAddress == "" || proxy.ProxyPort == 0 { + return nil + } + + credentials := "" + if proxy.ProxyLogin != nil { + credentials += *proxy.ProxyLogin + if proxy.ProxyPassword != nil { + credentials += ":" + *proxy.ProxyPassword + } + credentials += "@" + } + + proxyStr := fmt.Sprintf("%s://%s%s:%d", proxy.ProxyType, credentials, proxy.ProxyAddress, proxy.ProxyPort) + return &proxyStr +} + func (a *antigateV2Api) createTask(request struct { Task map[string]any `json:"task"` }) any { @@ -157,10 +193,11 @@ func (a *antigateV2Api) createTask(request struct { if !ok { return errorTaskAbsent } + taskType = strings.ToLower(taskType) - var id uint32 + var satiTask sati.AnyTask - switch strings.ToLower(taskType) { + switch taskType { case "turnstiletask", "turnstiletaskproxyless": var task struct { WebsiteURL string `json:"websiteURL"` @@ -168,21 +205,35 @@ func (a *antigateV2Api) createTask(request struct { Action *string `json:"action"` } mapstructure.Decode(request.Task, &task) - id = a.ctx.Registry.CreateTask(&sati.TurnstileTask{ + satiTask = &sati.TurnstileTask{ PageUrl: task.WebsiteURL, SiteKey: task.WebsiteKey, Action: task.Action, - }) + } + if taskType == "turnstiletask" { + proxy := collectAntiGateV2Proxy(request.Task) + if proxy == nil { + return errorProxyConnectRefused + } + satiTask.(*sati.TurnstileTask).Proxy = proxy + } case "recaptchav2task", "recaptchav2taskproxyless": var task struct { WebsiteURL string `json:"websiteURL"` WebsiteKey string `json:"websiteKey"` } mapstructure.Decode(request.Task, &task) - id = a.ctx.Registry.CreateTask(&sati.ReCaptcha2Task{ + satiTask = &sati.ReCaptcha2Task{ PageUrl: task.WebsiteURL, SiteKey: task.WebsiteKey, - }) + } + if taskType == "funcaptchatask" { + proxy := collectAntiGateV2Proxy(request.Task) + if proxy == nil { + return errorProxyConnectRefused + } + satiTask.(*sati.ReCaptcha2Task).Proxy = proxy + } case "funcaptchatask", "funcaptchataskproxyless": var task struct { WebsiteURL string `json:"websiteURL"` @@ -190,15 +241,24 @@ func (a *antigateV2Api) createTask(request struct { Data map[string]string `json:"data"` } mapstructure.Decode(request.Task, &task) - id = a.ctx.Registry.CreateTask(&sati.FunCaptchaTask{ + satiTask = &sati.FunCaptchaTask{ PageUrl: task.WebsiteURL, SiteKey: task.WebsitePublicKey, Data: task.Data, - }) + } + if taskType == "funcaptchatask" { + proxy := collectAntiGateV2Proxy(request.Task) + if proxy == nil { + return errorProxyConnectRefused + } + satiTask.(*sati.FunCaptchaTask).Proxy = proxy + } default: return errorTaskNotSupported } + id := a.ctx.Registry.CreateTask(satiTask) + return &struct { ErrorId uint32 `json:"errorId"` TaskId uint32 `json:"taskId"` diff --git a/api/CapSolver.go b/api/CapSolver.go index fc1e82c..b31d11c 100644 --- a/api/CapSolver.go +++ b/api/CapSolver.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net/http" + "net/url" "reflect" "strconv" "strings" @@ -130,6 +131,42 @@ func (a *capSolverApi) getTaskResult(request struct { return response } +func collectCapSolverProxy(task any) *string { + antigateLike := collectAntiGateV2Proxy(task) + if antigateLike != nil { + return antigateLike + } + + input, _ := task.(map[string]any)["proxy"].(string) + if input == "" { + return nil + } + + isUrl := strings.Contains(input, "://") + if isUrl { + // validate it + url, err := url.Parse(input) + if err != nil { + return nil + } + output := url.String() + return &output + } + + fragments := strings.Split(input, ":") + var output string + switch len(fragments) { + case 5: // proto:host:port:user:pwd + output = fmt.Sprintf("%s://%s:%s@%s:%s", fragments[0], fragments[3], fragments[4], fragments[1], fragments[2]) + case 4: // host:port:user:pwd, http used by default + output = fmt.Sprintf("http://%s:%s@%s:%s", fragments[2], fragments[3], fragments[0], fragments[1]) + default: + return nil + } + + return &output +} + func (a *capSolverApi) createTask(request struct { Task map[string]any `json:"task"` }) any { @@ -138,7 +175,7 @@ func (a *capSolverApi) createTask(request struct { return csErrorInvalidTaskData } - var id uint32 + var satiTask sati.AnyTask switch strings.ToLower(taskType) { case "anticloudflaretask": @@ -151,22 +188,36 @@ func (a *capSolverApi) createTask(request struct { } `json:"metadata"` } mapstructure.Decode(request.Task, &task) - id = a.ctx.Registry.CreateTask(&sati.TurnstileTask{ + + proxy := collectCapSolverProxy(request.Task) + if proxy == nil { + return csErrorInvalidTaskData + } + + satiTask = &sati.TurnstileTask{ PageUrl: task.WebsiteURL, SiteKey: task.WebsiteKey, Action: task.Metadata.Action, CData: task.Metadata.CData, - }) + Proxy: proxy, + } case "recaptchav2task", "recaptchav2taskproxyless": var task struct { WebsiteURL string `json:"websiteURL"` WebsiteKey string `json:"websiteKey"` } mapstructure.Decode(request.Task, &task) - id = a.ctx.Registry.CreateTask(&sati.ReCaptcha2Task{ + satiTask = &sati.ReCaptcha2Task{ PageUrl: task.WebsiteURL, SiteKey: task.WebsiteKey, - }) + } + if taskType == "recaptchav2task" { + proxy := collectCapSolverProxy(request.Task) + if proxy == nil { + return csErrorInvalidTaskData + } + satiTask.(*sati.ReCaptcha2Task).Proxy = proxy + } case "funcaptchatask", "funcaptchataskproxyless": var task struct { WebsiteURL string `json:"websiteURL"` @@ -174,15 +225,24 @@ func (a *capSolverApi) createTask(request struct { Data map[string]string `json:"data"` } mapstructure.Decode(request.Task, &task) - id = a.ctx.Registry.CreateTask(&sati.FunCaptchaTask{ + satiTask = &sati.FunCaptchaTask{ PageUrl: task.WebsiteURL, SiteKey: task.WebsitePublicKey, Data: task.Data, - }) + } + if taskType == "funcaptchatask" { + proxy := collectCapSolverProxy(request.Task) + if proxy == nil { + return csErrorInvalidTaskData + } + satiTask.(*sati.FunCaptchaTask).Proxy = proxy + } default: return csErrorTaskNotSupported } + id := a.ctx.Registry.CreateTask(satiTask) + return &struct { ErrorId uint32 `json:"errorId"` TaskId string `json:"taskId"` diff --git a/api/RuCaptcha.go b/api/RuCaptcha.go index b083768..005860c 100644 --- a/api/RuCaptcha.go +++ b/api/RuCaptcha.go @@ -2,6 +2,7 @@ package api import ( "encoding/json" + "fmt" "net/http" "net/url" "strconv" @@ -90,6 +91,21 @@ func parsePhpAssociativeArray(values map[string][]string, arrayName string) map[ return result } +func extractRuCaptchaProxy(params url.Values) *string { + if !params.Has("proxytype") || !params.Has("proxy") { + return nil + } + + proxyUrl := fmt.Sprintf("%s://%s", params.Get("proxytype"), params.Get("proxy")) + url, err := url.Parse(proxyUrl) + if err != nil { + return nil + } + + formatted := url.String() + return &formatted +} + func (a *ruCaptchaApi) endpointIn(params url.Values) ruCaptchaResponse { var id uint32 switch params.Get("method") { @@ -118,6 +134,7 @@ func (a *ruCaptchaApi) endpointIn(params url.Values) ruCaptchaResponse { id = a.ctx.Registry.CreateTask(&sati.ReCaptcha2Task{ PageUrl: pageUrl, SiteKey: siteKey, + Proxy: extractRuCaptchaProxy(params), }) case "turnstile": pageUrl := params.Get("pageurl") @@ -142,6 +159,7 @@ func (a *ruCaptchaApi) endpointIn(params url.Values) ruCaptchaResponse { PageUrl: pageUrl, Action: action, CData: cData, + Proxy: extractRuCaptchaProxy(params), }) case "funcaptcha": siteKey := params.Get("publickey") @@ -162,6 +180,7 @@ func (a *ruCaptchaApi) endpointIn(params url.Values) ruCaptchaResponse { PageUrl: pageUrl, ServiceUrl: serviceUrl, Data: data, + Proxy: extractRuCaptchaProxy(params), }) default: return &simpleResponse{0, "ERROR_ZERO_CAPTCHA_FILESIZE"} diff --git a/go.mod b/go.mod index 4491d2e..7b47d60 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,7 @@ module git.sati.ac/sati.ac/bridge go 1.20 -require git.sati.ac/sati.ac/sati-go v0.0.0-20230713145537-57719018ca00 +require git.sati.ac/sati.ac/sati-go v0.0.0-20230725102846-8e6b00348696 require ( github.com/gorilla/websocket v1.5.0 // indirect diff --git a/go.sum b/go.sum index 573f90a..a946926 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ git.sati.ac/sati.ac/sati-go v0.0.0-20230630184329-03a405a25122 h1:Uff2QZeRDk+3cm git.sati.ac/sati.ac/sati-go v0.0.0-20230630184329-03a405a25122/go.mod h1:dsLvwV5+2YUjWRAuTYFf/EMvoH/twUu/NWA0t5Yl3pQ= git.sati.ac/sati.ac/sati-go v0.0.0-20230713145537-57719018ca00 h1:emjsk5AubG3EiCbiG0WAG6xAZXoNNwO/yteYj/J0TqA= git.sati.ac/sati.ac/sati-go v0.0.0-20230713145537-57719018ca00/go.mod h1:dsLvwV5+2YUjWRAuTYFf/EMvoH/twUu/NWA0t5Yl3pQ= +git.sati.ac/sati.ac/sati-go v0.0.0-20230725102846-8e6b00348696 h1:ld33XeJOBd1skmlVz/TQrcuZPaTIuBt+J1n52oRfPAo= +git.sati.ac/sati.ac/sati-go v0.0.0-20230725102846-8e6b00348696/go.mod h1:dsLvwV5+2YUjWRAuTYFf/EMvoH/twUu/NWA0t5Yl3pQ= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=