Browse Source

fix: handle error returns and unsafe type assertions in errcheck linter

pull/102/head
Moroka8 3 months ago
parent
commit
79d46b2e65
  1. 9
      .golangci.yml
  2. 231
      client/main.go
  3. 4
      client/manual_captcha.go
  4. 1
      server/main.go

9
.golangci.yml

@ -10,6 +10,11 @@ linters:
errcheck: errcheck:
check-type-assertions: true check-type-assertions: true
check-blank: true check-blank: true
exclude-functions:
- (net.PacketConn).WriteTo
- (net.Conn).Write
- encoding/json.MarshalIndent
- (*github.com/pion/dtls/v3.Conn).SetDeadline
govet: govet:
disable: disable:
- fieldalignment - fieldalignment
@ -47,6 +52,10 @@ linters:
issues: issues:
max-issues-per-linter: 0 max-issues-per-linter: 0
max-same-issues: 0 max-same-issues: 0
exclude-rules:
- linters:
- errcheck
source: "doRequest|packetPool\\.Get"
formatters: formatters:
exclusions: exclusions:
generated: lax generated: lax

231
client/main.go

@ -236,7 +236,7 @@ type VkCaptchaError struct {
ErrorMsg string ErrorMsg string
CaptchaSid string CaptchaSid string
CaptchaImg string CaptchaImg string
RedirectUri string RedirectURI string
IsSoundCaptchaAvailable bool IsSoundCaptchaAvailable bool
SessionToken string SessionToken string
CaptchaTs string CaptchaTs string
@ -244,58 +244,96 @@ type VkCaptchaError struct {
} }
func ParseVkCaptchaError(errData map[string]interface{}) *VkCaptchaError { func ParseVkCaptchaError(errData map[string]interface{}) *VkCaptchaError {
codeFloat, _ := errData["error_code"].(float64) // Extract error_code
code := int(codeFloat) codeFloat, ok := errData["error_code"].(float64)
if !ok {
redirectUri, _ := errData["redirect_uri"].(string) log.Printf("missing error_code in captcha error data")
captchaSid, _ := errData["captcha_sid"].(string) return nil
if captchaSid == "" { }
if sidNum, ok := errData["captcha_sid"].(float64); ok { code := int(codeFloat)
captchaSid = fmt.Sprintf("%.0f", sidNum)
} // Extract redirect_uri
} RedirectURI, ok := errData["redirect_uri"].(string)
if !ok {
captchaImg, _ := errData["captcha_img"].(string) log.Printf("missing redirect_uri in captcha error data")
errorMsg, _ := errData["error_msg"].(string) return nil
}
var sessionToken string
if redirectUri != "" { // Extract captcha_sid
if parsed, err := neturl.Parse(redirectUri); err == nil { captchaSid, ok := errData["captcha_sid"].(string)
sessionToken = parsed.Query().Get("session_token") if !ok {
} // try numeric
} if sidNum, ok2 := errData["captcha_sid"].(float64); ok2 {
captchaSid = fmt.Sprintf("%.0f", sidNum)
isSound, _ := errData["is_sound_captcha_available"].(bool) } else {
log.Printf("missing captcha_sid in captcha error data")
var captchaTs string return nil
if tsFloat, ok := errData["captcha_ts"].(float64); ok { }
captchaTs = fmt.Sprintf("%.0f", tsFloat) }
} else if tsStr, ok := errData["captcha_ts"].(string); ok {
captchaTs = tsStr // Extract captcha_img
} captchaImg, ok := errData["captcha_img"].(string)
if !ok {
var captchaAttempt string log.Printf("missing captcha_img in captcha error data")
if attFloat, ok := errData["captcha_attempt"].(float64); ok { return nil
captchaAttempt = fmt.Sprintf("%.0f", attFloat) }
} else if attStr, ok := errData["captcha_attempt"].(string); ok {
captchaAttempt = attStr // Extract error_msg
} errorMsg, ok := errData["error_msg"].(string)
if !ok {
return &VkCaptchaError{ log.Printf("missing error_msg in captcha error data")
ErrorCode: code, return nil
ErrorMsg: errorMsg, }
CaptchaSid: captchaSid,
CaptchaImg: captchaImg, // Extract session token if redirect_uri present
RedirectUri: redirectUri, var sessionToken string
IsSoundCaptchaAvailable: isSound, if RedirectURI != "" {
SessionToken: sessionToken, if parsed, err := neturl.Parse(RedirectURI); err == nil {
CaptchaTs: captchaTs, sessionToken = parsed.Query().Get("session_token")
CaptchaAttempt: captchaAttempt, } else {
log.Printf("failed to parse redirect_uri: %v", err)
return nil
}
}
// Extract is_sound_captcha_available
isSound, ok := errData["is_sound_captcha_available"].(bool)
if !ok {
isSound = false
}
// Extract captcha_ts
var captchaTs string
if tsFloat, ok := errData["captcha_ts"].(float64); ok {
captchaTs = fmt.Sprintf("%.0f", tsFloat)
} else if tsStr, ok := errData["captcha_ts"].(string); ok {
captchaTs = tsStr
}
// Extract captcha_attempt
var captchaAttempt string
if attFloat, ok := errData["captcha_attempt"].(float64); ok {
captchaAttempt = fmt.Sprintf("%.0f", attFloat)
} else if attStr, ok := errData["captcha_attempt"].(string); ok {
captchaAttempt = attStr
}
// Build VkCaptchaError
return &VkCaptchaError{
ErrorCode: code,
ErrorMsg: errorMsg,
CaptchaSid: captchaSid,
CaptchaImg: captchaImg,
RedirectURI: RedirectURI,
IsSoundCaptchaAvailable: isSound,
SessionToken: sessionToken,
CaptchaTs: captchaTs,
CaptchaAttempt: captchaAttempt,
} }
} }
func (e *VkCaptchaError) IsCaptchaError() bool { func (e *VkCaptchaError) IsCaptchaError() bool {
return e.ErrorCode == 14 && e.RedirectUri != "" && e.SessionToken != "" return e.ErrorCode == 14 && e.RedirectURI != "" && e.SessionToken != ""
} }
func solveVkCaptcha(ctx context.Context, captchaErr *VkCaptchaError, streamID int, client tlsclient.HttpClient, profile Profile) (string, error) { func solveVkCaptcha(ctx context.Context, captchaErr *VkCaptchaError, streamID int, client tlsclient.HttpClient, profile Profile) (string, error) {
@ -304,11 +342,11 @@ func solveVkCaptcha(ctx context.Context, captchaErr *VkCaptchaError, streamID in
if captchaErr.SessionToken == "" { if captchaErr.SessionToken == "" {
return "", fmt.Errorf("no session_token in redirect_uri for auto-solve") return "", fmt.Errorf("no session_token in redirect_uri for auto-solve")
} }
if captchaErr.RedirectUri == "" { if captchaErr.RedirectURI == "" {
return "", fmt.Errorf("no redirect_uri for auto-solve") return "", fmt.Errorf("no redirect_uri for auto-solve")
} }
powInput, difficulty, err := fetchPowInput(ctx, captchaErr.RedirectUri, client, profile) powInput, difficulty, err := fetchPowInput(ctx, captchaErr.RedirectURI, client, profile)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to fetch PoW input: %w", err) return "", fmt.Errorf("failed to fetch PoW input: %w", err)
} }
@ -327,14 +365,14 @@ func solveVkCaptcha(ctx context.Context, captchaErr *VkCaptchaError, streamID in
return successToken, nil return successToken, nil
} }
func fetchPowInput(ctx context.Context, redirectUri string, client tlsclient.HttpClient, profile Profile) (string, int, error) { func fetchPowInput(ctx context.Context, RedirectURI string, client tlsclient.HttpClient, profile Profile) (string, int, error) {
parsedURL, err := neturl.Parse(redirectUri) parsedURL, err := neturl.Parse(RedirectURI)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }
domain := parsedURL.Hostname() domain := parsedURL.Hostname()
req, err := fhttp.NewRequestWithContext(ctx, "GET", redirectUri, nil) req, err := fhttp.NewRequestWithContext(ctx, "GET", RedirectURI, nil)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
} }
@ -394,7 +432,10 @@ func solvePoW(powInput string, difficulty int) string {
func callCaptchaNotRobot(ctx context.Context, sessionToken, hash string, streamID int, client tlsclient.HttpClient, profile Profile) (string, error) { func callCaptchaNotRobot(ctx context.Context, sessionToken, hash string, streamID int, client tlsclient.HttpClient, profile Profile) (string, error) {
vkReq := func(method string, postData string) (map[string]interface{}, error) { vkReq := func(method string, postData string) (map[string]interface{}, error) {
reqURL := "https://api.vk.ru/method/" + method + "?v=5.131" reqURL := "https://api.vk.ru/method/" + method + "?v=5.131"
parsedURL, _ := neturl.Parse(reqURL) parsedURL, err := neturl.Parse(reqURL)
if err != nil {
return nil, fmt.Errorf("parse request URL: %w", err)
}
domain := parsedURL.Hostname() domain := parsedURL.Hostname()
req, err := fhttp.NewRequestWithContext(ctx, "POST", reqURL, strings.NewReader(postData)) req, err := fhttp.NewRequestWithContext(ctx, "POST", reqURL, strings.NewReader(postData))
@ -748,7 +789,10 @@ func getTokenChain(ctx context.Context, link string, streamID int, creds VKCrede
log.Printf("[STREAM %d] [VK Auth] Connecting Identity - Name: %s | User-Agent: %s", streamID, name, profile.UserAgent) log.Printf("[STREAM %d] [VK Auth] Connecting Identity - Name: %s | User-Agent: %s", streamID, name, profile.UserAgent)
doRequest := func(data string, url string) (resp map[string]interface{}, err error) { doRequest := func(data string, url string) (resp map[string]interface{}, err error) {
parsedURL, _ := neturl.Parse(url) parsedURL, err := neturl.Parse(url)
if err != nil {
return nil, fmt.Errorf("parse request URL: %w", err)
}
domain := parsedURL.Hostname() domain := parsedURL.Hostname()
req, err := fhttp.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer([]byte(data))) req, err := fhttp.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer([]byte(data)))
@ -808,7 +852,10 @@ func getTokenChain(ctx context.Context, link string, streamID int, creds VKCrede
// Token 1 -> getCallPreview // Token 1 -> getCallPreview
data = fmt.Sprintf("vk_join_link=https://vk.com/call/join/%s&fields=photo_200&access_token=%s", link, token1) data = fmt.Sprintf("vk_join_link=https://vk.com/call/join/%s&fields=photo_200&access_token=%s", link, token1)
_, _ = doRequest(data, "https://api.vk.ru/method/calls.getCallPreview?v=5.275&client_id="+creds.ClientID) _, err = doRequest(data, "https://api.vk.ru/method/calls.getCallPreview?v=5.275&client_id="+creds.ClientID)
if err != nil {
log.Printf("[STREAM %d] [VK Auth] Warning: getCallPreview failed: %v", streamID, err)
}
vkDelayRandom(200, 400) vkDelayRandom(200, 400)
@ -833,7 +880,7 @@ func getTokenChain(ctx context.Context, link string, streamID int, creds VKCrede
if attempt < maxAutoAttempts { if attempt < maxAutoAttempts {
// Auto Solve Attempts // Auto Solve Attempts
if captchaErr.SessionToken != "" && captchaErr.RedirectUri != "" { if captchaErr.SessionToken != "" && captchaErr.RedirectURI != "" {
successToken, solveErr = solveVkCaptcha(ctx, captchaErr, streamID, client, profile) successToken, solveErr = solveVkCaptcha(ctx, captchaErr, streamID, client, profile)
if solveErr != nil { if solveErr != nil {
log.Printf("[STREAM %d] [Captcha] Auto solve failed: %v", streamID, solveErr) log.Printf("[STREAM %d] [Captcha] Auto solve failed: %v", streamID, solveErr)
@ -857,8 +904,8 @@ func getTokenChain(ctx context.Context, link string, streamID int, creds VKCrede
go func() { go func() {
var t, k string var t, k string
var e error var e error
if captchaErr.RedirectUri != "" { if captchaErr.RedirectURI != "" {
t, e = solveCaptchaViaProxy(captchaErr.RedirectUri, dialer) t, e = solveCaptchaViaProxy(captchaErr.RedirectURI, dialer)
} else if captchaErr.CaptchaImg != "" { } else if captchaErr.CaptchaImg != "" {
k, e = solveCaptchaViaHTTP(captchaErr.CaptchaImg) k, e = solveCaptchaViaHTTP(captchaErr.CaptchaImg)
} else { } else {
@ -950,7 +997,10 @@ func getTokenChain(ctx context.Context, link string, streamID int, creds VKCrede
if err != nil { if err != nil {
return "", "", "", err return "", "", "", err
} }
token3 := resp["session_key"].(string) token3, ok := resp["session_key"].(string)
if !ok {
return "", "", "", fmt.Errorf("missing session_key in response: %v", resp)
}
vkDelayRandom(100, 150) vkDelayRandom(100, 150)
@ -961,11 +1011,26 @@ func getTokenChain(ctx context.Context, link string, streamID int, creds VKCrede
return "", "", "", err return "", "", "", err
} }
ts := resp["turn_server"].(map[string]interface{}) tsRaw, ok := resp["turn_server"].(map[string]interface{})
user := ts["username"].(string) if !ok {
pass := ts["credential"].(string) return "", "", "", fmt.Errorf("missing turn_server in response: %v", resp)
urls := ts["urls"].([]interface{}) }
urlStr := urls[0].(string) user, ok := tsRaw["username"].(string)
if !ok {
return "", "", "", fmt.Errorf("missing username in turn_server")
}
pass, ok := tsRaw["credential"].(string)
if !ok {
return "", "", "", fmt.Errorf("missing credential in turn_server")
}
urlsRaw, ok := tsRaw["urls"].([]interface{})
if !ok || len(urlsRaw) == 0 {
return "", "", "", fmt.Errorf("missing or empty urls in turn_server")
}
urlStr, ok := urlsRaw[0].(string)
if !ok {
return "", "", "", fmt.Errorf("turn server url is not a string")
}
clean := strings.Split(urlStr, "?")[0] clean := strings.Split(urlStr, "?")[0]
address := strings.TrimPrefix(strings.TrimPrefix(clean, "turn:"), "turns:") address := strings.TrimPrefix(strings.TrimPrefix(clean, "turn:"), "turns:")
@ -1315,7 +1380,7 @@ func dtlsFunc(ctx context.Context, conn net.PacketConn, peer *net.UDPAddr) (net.
func oneDtlsConnection(ctx context.Context, peer *net.UDPAddr, listenConn net.PacketConn, inboundChan <-chan *UDPPacket, connchan chan<- net.PacketConn, okchan chan<- struct{}, streamID int) error { func oneDtlsConnection(ctx context.Context, peer *net.UDPAddr, listenConn net.PacketConn, inboundChan <-chan *UDPPacket, connchan chan<- net.PacketConn, okchan chan<- struct{}, streamID int) error {
time.Sleep(time.Duration(rand.Intn(400)+100) * time.Millisecond) time.Sleep(time.Duration(rand.Intn(400)+100) * time.Millisecond)
var err error
dtlsctx, dtlscancel := context.WithCancel(ctx) dtlsctx, dtlscancel := context.WithCancel(ctx)
defer dtlscancel() defer dtlscancel()
@ -1353,7 +1418,9 @@ func oneDtlsConnection(ctx context.Context, peer *net.UDPAddr, listenConn net.Pa
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
wg.Add(1) wg.Add(1)
context.AfterFunc(dtlsctx, func() { context.AfterFunc(dtlsctx, func() {
_ = dtlsConn.SetDeadline(time.Now()) if err := dtlsConn.SetDeadline(time.Now()); err != nil {
log.Printf("[STREAM %d] Warning: SetDeadline failed: %v", streamID, err)
}
}) })
go func() { go func() {
@ -1381,7 +1448,11 @@ func oneDtlsConnection(ctx context.Context, peer *net.UDPAddr, listenConn net.Pa
// Send back to the active WG client // Send back to the active WG client
if peerAddr := activeLocalPeer.Load(); peerAddr != nil { if peerAddr := activeLocalPeer.Load(); peerAddr != nil {
_, _ = listenConn.WriteTo(buf[:n], peerAddr.(net.Addr)) if addr, ok := peerAddr.(net.Addr); ok {
if _, err := listenConn.WriteTo(buf[:n], addr); err != nil {
log.Printf("[STREAM %d] failed to forward packet to local peer: %v", streamID, err)
}
}
} }
} }
}() }()
@ -1574,9 +1645,10 @@ func oneTurnConnection(ctx context.Context, turnParams *turnParams, peer *net.UD
continue continue
} }
_, err1 = conn2.WriteTo(buf[:n], addr1.(net.Addr)) if addr, ok := addr1.(net.Addr); ok {
if err1 != nil { if _, err := conn2.WriteTo(buf[:n], addr); err != nil {
return return
}
} }
} }
}() }()
@ -1766,7 +1838,12 @@ func main() {
go func() { go func() {
for { for {
pkt := packetPool.Get().(*UDPPacket) pktIface := packetPool.Get()
pkt, ok := pktIface.(*UDPPacket)
if !ok {
log.Printf("packetPool returned unexpected type: %T", pktIface)
continue
}
nRead, addr, err := listenConn.ReadFrom(pkt.Data) nRead, addr, err := listenConn.ReadFrom(pkt.Data)
if err != nil { if err != nil {
return return
@ -1774,7 +1851,13 @@ func main() {
// Save the local WireGuard peer address // Save the local WireGuard peer address
current := activeLocalPeer.Load() current := activeLocalPeer.Load()
if current == nil || current.(net.Addr).String() != addr.String() { if current == nil {
activeLocalPeer.Store(addr)
} else if addrStr, ok := current.(net.Addr); ok {
if addrStr.String() != addr.String() {
activeLocalPeer.Store(addr)
}
} else {
activeLocalPeer.Store(addr) activeLocalPeer.Store(addr)
} }

4
client/manual_captcha.go

@ -514,8 +514,8 @@ func solveCaptchaViaProxy(redirectURI string, dialer *dnsdialer.Dialer) (string,
}) })
mux.HandleFunc("/generic_proxy", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/generic_proxy", func(w http.ResponseWriter, r *http.Request) {
targetAuthUrl := r.URL.Query().Get("proxy_url") targetAuthURL := r.URL.Query().Get("proxy_url")
targetParsed, err := neturl.Parse(targetAuthUrl) targetParsed, err := neturl.Parse(targetAuthURL)
if err != nil || targetParsed.Host == "" { if err != nil || targetParsed.Host == "" {
http.Error(w, "Bad URL", http.StatusBadRequest) http.Error(w, "Bad URL", http.StatusBadRequest)
return return

1
server/main.go

@ -20,7 +20,6 @@ import (
"github.com/xtaci/smux" "github.com/xtaci/smux"
) )
const idleTimeout = 2 * time.Minute
func main() { func main() {
listen := flag.String("listen", "0.0.0.0:56000", "listen on ip:port") listen := flag.String("listen", "0.0.0.0:56000", "listen on ip:port")

Loading…
Cancel
Save