PojavLauncherTeam / PojavLauncher

A Minecraft: Java Edition Launcher for Android and iOS based on Boardwalk. This repository contains source code for Android platform.
GNU General Public License v3.0
5.9k stars 1.18k forks source link

[F-Req] No IPv6 Support #5125

Open Xinhe9 opened 3 months ago

Xinhe9 commented 3 months ago

Describe the bug

I can't use IPv6 address to connect my server normally. It works ow Windows 11 but can't work in Pojavlauncher. Please support it Thanks

The log file and images/videos

Screenshot_20240203_174327 (1) Screenshot_20240203_174419_net kdt pojavlaunch debug Screenshot_20240203_174438_net kdt pojavlaunch debug Screenshot_20240203_174440_net kdt pojavlaunch debug

Steps To Reproduce

1. Start Pojavlauncher
2. Start games
3. Multiplayer
4. Add server and the address is [address]:25565
5. Can't connect to server

Expected Behavior

No

Platform

- Device model: 
- CPU architecture: arm64
- Android version: 10
- PojavLauncher version: edelweiss-20240127-bb16bff-feat/profile_icon_customization

Anything else?

Hope the Pojavlauncher can better and better!!

Xinhe9 commented 3 months ago

Describe the bug

I can't use IPv6 address to connect my server normally. It works on Windows 11 but can't work in Pojavlauncher. Please support it. Thanks

The log file and images/videos

Screenshot_20240203_174327 (1) Screenshot_20240203_174419_net kdt pojavlaunch debug Screenshot_20240203_174438_net kdt pojavlaunch debug Screenshot_20240203_174440_net kdt pojavlaunch debug

Steps To Reproduce

1. Start Pojavlauncher
2. Start games
3. Multiplayer
4. Add server and the address is [address]:25565
5. Can't connect to server

Expected Behavior

No

Platform

- Device model: 
- CPU architecture: arm64
- Android version: 10
- PojavLauncher version: edelweiss-20240127-bb16bff-feat/profile_icon_customization

Anything else?

Hope the Pojavlauncher can better and better!!

DGHOSTI commented 3 months ago

有别的办法了吗,这个Minecraft好像无法解析ipv6

Xinhe9 commented 3 months ago

有别的办法了吗,这个Minecraft好像无法解析ipv6

没有,貌似Pojavlauncher Team也没有做出修改,不知道其他启动器可不可以

DGHOSTI commented 3 months ago

有别的办法了吗,这个Minecraft好像无法解析ipv6

没有,貌似Pojavlauncher Team也没有做出修改,不知道其他启动器可不可以

fcl启动器也不行

Xinhe9 commented 3 months ago

有别的办法了吗,这个Minecraft好像无法解析ipv6

没有,貌似Pojavlauncher Team也没有做出修改,不知道其他启动器可不可以

fcl启动器也不行

我见管理员打了几个标签,那就只能等Pojav官方做出改进了

Mathias-Boulay commented 2 months ago

We are open on a PR for this issue

Xinhe9 commented 2 months ago

We are open on a PR for this issue

Thank you. Most Chinese people haven't got public IPv4 address, but most Chinese people have got public IPv6 address. I also have got an public IPV6 address and I have a Minecraft server. I hope that more people can join my server. My friends only have mobile phones, so they have to use Pojavlauncher. I think one day it will come true. Love from Chinese players.❤️❤️

Xinhe9 commented 2 weeks ago

We are open on a PR for this issue

Hello, I found that some old versions of Pojavlauncher can use IPv6. I think Pojavlauncher can do better. However, I wish Pojavlauncher can let more people love Minecraft.

nexplorer-3e commented 6 days ago

We are open on a PR for this issue

Hello, I found that some old versions of Pojavlauncher can use IPv6. I think Pojavlauncher can do better. However, I wish Pojavlauncher can let more people love Minecraft.

Hi, can you provide the version that with ipv6 working? If it's a regression maybe we can fix it

Xinhe9 commented 6 days ago

We are open on a PR for this issue

Hello, I found that some old versions of Pojavlauncher can use IPv6. I think Pojavlauncher can do better. However, I wish Pojavlauncher can let more people love Minecraft.

Hi, can you provide the version that with ipv6 working? If it's a regression maybe we can fix it

I tried on my Android 6.0 device, the version of Pojavlauncher was edelweiss-20230928-9484d0c-v3_openjoy .

Xinhe9 commented 6 days ago

We are open on a PR for this issue

Hello, I found that some old versions of Pojavlauncher can use IPv6. I think Pojavlauncher can do better. However, I wish Pojavlauncher can let more people love Minecraft.

Hi, can you provide the version that with ipv6 working? If it's a regression maybe we can fix it

在我的安卓6.0手机上可使用ipv6,版本是20230928的9484d0c

nexplorer-3e commented 6 days ago

We are open on a PR for this issue

Hello, I found that some old versions of Pojavlauncher can use IPv6. I think Pojavlauncher can do better. However, I wish Pojavlauncher can let more people love Minecraft.

Hi, can you provide the version that with ipv6 working? If it's a regression maybe we can fix it

I tried on my Android 6.0 device, the version of Pojavlauncher was edelweiss-20230928-9484d0c-v3_openjoy .

Is the play version? Tested 1.20.1 with no luck. How do you get jre17? My phone is on Android 14 and I am wondering if it's related to kernel issue

Xinhe9 commented 5 days ago

We are open on a PR for this issue

Hello, I found that some old versions of Pojavlauncher can use IPv6. I think Pojavlauncher can do better. However, I wish Pojavlauncher can let more people love Minecraft.

Hi, can you provide the version that with ipv6 working? If it's a regression maybe we can fix it

I tried on my Android 6.0 device, the version of Pojavlauncher was edelweiss-20230928-9484d0c-v3_openjoy.

Is the play version? Tested 1.20.1 with no luck. How do you get jre17? My phone is on Android 14 and I am wondering if it's related to kernel issue

I tested again just now, however, it worked again. IMG_20240527_213500.jpg

IMG_20240527_213523.jpg

IMG_20240527_213511_edit_4627821311272.jpg

Screenshot_20240527_213711.jpg

IMG_20240527_213751.jpg The Minecraft version is 1.20.4, I can join my server. IMG_20240527_214522.jpg

I think maybe it's Android's kernel issue.

nexplorer-3e commented 4 days ago

I've managed to reproduce the exception by using java.nio.channels.SocketChannel. Code here:

import java.net.InetSocketAddress;

import java.nio.channels.SocketChannel;

public class IPv6SocketChannelExample {
    public static void main(String[] args) {
        String host = "[::1]"; // Replace with the actual IPv6 address
        int port = 80; // Replace with the actual port number

        try {
            // Create a new SocketChannel
            SocketChannel socketChannel = SocketChannel.open();

            // Create an InetSocketAddress with an IPv6 address
            InetSocketAddress inetSocketAddress = new InetSocketAddress(host, port);

            // Connect the SocketChannel to the remote address
            socketChannel.connect(inetSocketAddress);

            // Use the socketChannel to read/write data
            // ...

            // Close the SocketChannel
            socketChannel.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

With javac <name>.java and jar -e <ClassName> -c -f test.jar <name>.class, you should get a jar which can be invoke in the launcher, which would raise UnsupportedAddressTypeException on my phone. (You can also invoke the .class in termux and it will give you same exception, but with root it would work (throws "connection refused" as expected)) I may provide another netty example that i copied from another hello-world project, which can reproduce almost same stacktrace that MC does. For reference here is the jar: netty-helloworld.jar.gz

Xinhe9 commented 4 days ago

I can't understand your meaning. Do you mean I should run the jar file on Pojavlauncher?

nexplorer-3e commented 3 days ago

I can't understand your meaning. Do you mean I should run the jar file on Pojavlauncher?

Nope i just provide a case to reproduce the issue 🥲

Xinhe9 commented 3 days ago

Nope i just provide a case to reproduce the issue 🥲

Oh sorry. By the way, are you Chinese?

Mathias-Boulay commented 3 days ago

@nexplorer-3e For reference, can you give us the stacktraces as well ?

nexplorer-3e commented 3 days ago

@nexplorer-3e For reference, can you give us the stacktraces as well ?

[00:04:26] [Server Pinger #1/WARN]: Failed to find a usable hardware address from the network interfaces; using random bytes: 7a:b2:f4:e6:4b:14:b4:9d
[00:04:31] [Render thread/INFO]: Connecting to 240e::67, 25565
[00:04:31] [Server Connector #1/ERROR]: Couldn't connect to server
java.nio.channels.UnsupportedAddressTypeException: null
    at sun.nio.ch.Net.checkAddress(Unknown Source) ~[?:?]
    at sun.nio.ch.SocketChannelImpl.checkRemote(Unknown Source) ~[?:?]
    at sun.nio.ch.SocketChannelImpl.connect(Unknown Source) ~[?:?]
    at io.netty.util.internal.SocketUtils$3.run(SocketUtils.java:91) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.util.internal.SocketUtils$3.run(SocketUtils.java:88) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
    at java.security.AccessController.doPrivileged(Unknown Source) ~[?:?]
    at io.netty.util.internal.SocketUtils.connect(SocketUtils.java:88) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.channel.socket.nio.NioSocketChannel.doConnect(NioSocketChannel.java:322) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.connect(AbstractNioChannel.java:248) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.channel.DefaultChannelPipeline$HeadContext.connect(DefaultChannelPipeline.java:1342) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeConnect(AbstractChannelHandlerContext.java:548) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.channel.AbstractChannelHandlerContext.connect(AbstractChannelHandlerContext.java:533) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.channel.AbstractChannelHandlerContext.connect(AbstractChannelHandlerContext.java:517) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.channel.DefaultChannelPipeline.connect(DefaultChannelPipeline.java:978) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.channel.AbstractChannel.connect(AbstractChannel.java:265) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.bootstrap.Bootstrap$3.run(Bootstrap.java:250) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:174) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:167) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:569) ~[netty-transport-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
    at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.82.Final.jar:4.1.82.Final]
    at java.lang.Thread.run(Unknown Source) ~[?:?]
[00:04:31] [Server Connector #1/ERROR]: Caught previously unhandled exception :
java.lang.NullPointerException: Cannot invoke "String.replaceAll(String, String)" because the return value of "java.lang.Exception.getMessage()" is null
    at etl$1.run(SourceFile:128) ~[1.20.1.jar:?]
nexplorer-3e commented 2 days ago

there are two ways to produce UnsupportedAddressTypeException in Net.checkAddress on jdk17:

    public static InetSocketAddress checkAddress(SocketAddress sa) {
        if (sa == null)
            throw new NullPointerException();
        if (!(sa instanceof InetSocketAddress))
            throw new UnsupportedAddressTypeException(); // ## needs arg
        InetSocketAddress isa = (InetSocketAddress)sa;
        if (isa.isUnresolved())
            throw new UnresolvedAddressException(); // ## needs arg
        InetAddress addr = isa.getAddress();
        if (!(addr instanceof Inet4Address || addr instanceof Inet6Address))
            throw new IllegalArgumentException("Invalid address type");
        return isa;
    }

    static InetSocketAddress checkAddress(SocketAddress sa, ProtocolFamily family) {
        InetSocketAddress isa = checkAddress(sa);
        if (family == StandardProtocolFamily.INET) {
            InetAddress addr = isa.getAddress();
            if (!(addr instanceof Inet4Address))
                throw new UnsupportedAddressTypeException();
        }
        return isa;
    }

inferred from the code, I wonder why InetSocketAddress is different from "Inet6SocketAddress". back to SocketChannelImpl.connect(), in which calls Net.checkAddress() with family:

InetSocketAddress isa = Net.checkAddress(sa, family);

https://github.com/openjdk/jdk17u/blob/master/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java#L816

what is the family? Peek the (another) constructor where assign the value, it may related to Net.isIPv6Available():

        if (family == INET6 && !Net.isIPv6Available()) {
            throw new UnsupportedOperationException("IPv6 not available");
        }

https://github.com/openjdk/jdk17u/blob/master/src/java.base/share/classes/sun/nio/ch/SocketChannelImpl.java#L138

so maybe we just neet to find out how Net.isIPv6Available() return false. After digging some code it points to a native function isIPv6Available0(), and then with jni's magic it finally get the implementation of IPv6_supported() in libnet. Brief call hierarchy:

isIPv6Available0(Net.java) -native-> Java_sun_nio_ch_Net_isIPv6Available0(Net.c) -JNI-> ipv6_available(net_util.c) -include net_util_md.h-> IPv6_supported(net_util_md.c)

the IPv6_supported() implementation in libnet is different from the one in Android ojluni, which have removed many detection mechanism. The most relevant change happened in https://android.googlesource.com/platform/libcore/+/ae218d9bdc8395ac0ed9278c86cff597915c2a7b%5E%21 . I copied the libnet impl, add tiny definition to make it can be compiled only single file:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
// #include <
#define JNI_TRUE 1
#define JNI_FALSE 0

typedef union {
    struct sockaddr     sa;
    struct sockaddr_in  sa4;
    struct sockaddr_in6 sa6;
} SOCKETADDRESS;
typedef int socklen_t;

void ipv6_supported();
int main() {
    ipv6_supported();
    return 0;
}

void ipv6_supported()
{
    int fd;
    void *ipv6_fn;
    SOCKETADDRESS sa;
    socklen_t sa_len = sizeof(SOCKETADDRESS);

    fd = socket(AF_INET6, SOCK_STREAM, 0) ;
    if (fd < 0) {
        /*
         *  TODO: We really cant tell since it may be an unrelated error
         *  for now we will assume that AF_INET6 is not available
         */
        fprintf(stderr, "socket AF_INET6 failed!\n");
    }

    /*
     * If fd 0 is a socket it means we may have been launched from inetd or
     * xinetd. If it's a socket then check the family - if it's an
     * IPv4 socket then we need to disable IPv6.
     */
    if (getsockname(0, &sa.sa, &sa_len) == 0) {
        if (sa.sa.sa_family == AF_INET) {
            close(fd);
            fprintf(stderr, "getsockname is not AF_INET6!\n");
        }
    }

    /**
     * Linux - check if any interface has an IPv6 address.
     * Don't need to parse the line - we just need an indication.
     */
#ifdef __linux__
    {
        FILE *fP = fopen("/proc/net/if_inet6", "r");
        char buf[255];
        char *bufP;

        if (fP == NULL) {
            close(fd);
            fprintf(stderr, "Cannot open /proc/net/if_inet6!\n");
            fprintf(stderr, "Skip buffer check for ENOENT\n");
            return;
        }
        bufP = fgets(buf, sizeof(buf), fP);
        fclose(fP);
        if (bufP == NULL) {
            close(fd);
            fprintf(stderr, "buffer of /proc/net/if_inet6 is NULL!\n");
        }
    }
#endif

    /*
     *  OK we may have the stack available in the kernel,
     *  we should also check if the APIs are available.
     */
    // ipv6_fn = JVM_FindLibraryEntry(RTLD_DEFAULT, "inet_pton");
    // if (ipv6_fn == NULL ) {
    //     return JNI_FALSE;
    // } else {
    //     return JNI_TRUE;
    // }
    close(fd);
    printf("IPv6 is supported!\n");
}

compiled with termux clang, and run without root, throws:

~ $ ./jv6test
Cannot open /proc/net/if_inet6!
FORTIFY: fgets: null FILE*
Aborted

(to avoid this case of unexpected sigabrt, the single file above has been edited) a termux issues have discussed the problem: new SELinux rules restricts the file access. https://github.com/termux/termux-app/issues/2993#issuecomment-1249815245 as ping6 is working, maybe we could just apply the modification above to jdk patch, and ipv6 would working.

@Mathias-Boulay