Suwayomi / Suwayomi-Server

A rewrite of Tachiyomi for the Desktop
Mozilla Public License 2.0
3.84k stars 198 forks source link

[Bug] SOCKS5 Proxy authentication does not work correctly #987

Closed AeonLucid closed 3 weeks ago

AeonLucid commented 1 month ago

Device information

Steps to reproduce

  1. Set up https://github.com/serjs/socks5-server. Follow the instructions to set a PROXY_USER and PROXY_PASSWORD.
  2. Configure the server.socks* properties in Suwayomi.
  3. Confirm that server.socksProxyUsername and server.socksProxyPassword are set.
  4. Try loading data.

Expected behavior

I expect the SOCKS5 authentication to work.

Actual behavior

It does not send the configured username and password.

Other details

Related pull request that implements SOCKS5 authentication and mentions not testing it https://github.com/Suwayomi/Suwayomi-Server/pull/883.

I didn't test the code against a real proxy but it should be fine according to the docs.

Relevant code in Suwayomi. https://github.com/Suwayomi/Suwayomi-Server/blob/eaffb2755cfae436f4c2b1affe9c125046c2cc55/server/src/main/kotlin/suwayomi/tachidesk/server/ServerSetup.kt#L259-L283

Debugging the issue

You can easily create a small Java application to verify that it does not work.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

public class Main {
    public static void main(String[] args) {
        System.setProperty("socksProxyHost", "127.0.0.1");
        System.setProperty("socksProxyPort", "1080");
        System.setProperty("socksProxyVersion", "5");
        System.setProperty("java.net.socks.username", "test");
        System.setProperty("java.net.socks.password", "123");

        // Open a connection to the URL and read the response
        try {
            URL url = new URL("https://httpbin.org/get");
            URLConnection urlConnection = url.openConnection();
            InputStream in = urlConnection.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            String line = null;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Placing a breakpoint in the JDK on the socks5 authenticate method, you can easily verify that it is not using those java.net.socks.username properties.

image

Googling around leads to https://stackoverflow.com/a/11460268

According to the documentation, the java.net.socks.username property is only consulted if there is no java.net.Authenticator set. Perhaps some other code in your JVM has set a default Authenticator? If you have appropriate permission, try setting your own default java.net.Authenticator.

Which in turn reveals the solution:

System.setProperty("socksProxyHost", "127.0.0.1");
System.setProperty("socksProxyPort", "1080");
System.setProperty("socksProxyVersion", "5");

Authenticator.setDefault(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication("test", "123".toCharArray());
    }
});