diff --git a/app/src/main/java/net/kaaass/zerotierfix/service/ZeroTierOneService.java b/app/src/main/java/net/kaaass/zerotierfix/service/ZeroTierOneService.java index 00c0a75..ee0e0d9 100644 --- a/app/src/main/java/net/kaaass/zerotierfix/service/ZeroTierOneService.java +++ b/app/src/main/java/net/kaaass/zerotierfix/service/ZeroTierOneService.java @@ -65,12 +65,8 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.io.BufferedReader; import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; import java.net.DatagramSocket; import java.net.Inet4Address; import java.net.Inet6Address; @@ -79,6 +75,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -111,39 +108,17 @@ public class ZeroTierOneService extends VpnService implements Runnable, EventLis private Thread udpThread; private Thread v4MulticastScanner = new Thread() { /* class com.zerotier.one.service.ZeroTierOneService.AnonymousClass1 */ - ArrayList subscriptions = new ArrayList<>(); + List subscriptions = new ArrayList<>(); @Override public void run() { Log.d(ZeroTierOneService.TAG, "IPv4 Multicast Scanner Thread Started."); while (!isInterrupted()) { try { - ArrayList arrayList = new ArrayList<>(); - try { - BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/net/igmp")); - while (true) { - boolean z = false; - while (true) { - String readLine = bufferedReader.readLine(); - if (readLine == null) { - break; - } - String[] split = readLine.split("\\s+", -1); - if (!z && split[1].equals("tun0")) { - z = true; - } else if (z && split[0].equals("")) { - arrayList.add(split[1]); - } - } - } - } catch (FileNotFoundException e) { - Log.e(ZeroTierOneService.TAG, "File Not Found: /proc/net/igmp", e); - } catch (IOException e) { - Log.e(ZeroTierOneService.TAG, "Error parsing /proc/net/igmp", e); - } + List groups = NetworkInfoUtils.listMulticastGroupOnInterface("tun0", false); ArrayList arrayList2 = new ArrayList<>(this.subscriptions); - ArrayList arrayList3 = new ArrayList<>(arrayList); + ArrayList arrayList3 = new ArrayList<>(groups); arrayList3.removeAll(arrayList2); for (String str : arrayList3) { try { @@ -161,7 +136,7 @@ public void run() { Log.e(ZeroTierOneService.TAG, e.toString(), e); } } - arrayList2.removeAll(new ArrayList<>(arrayList)); + arrayList2.removeAll(new ArrayList<>(groups)); for (String str2 : arrayList2) { try { byte[] hexStringToByteArray2 = StringUtils.hexStringToBytes(str2); @@ -178,10 +153,11 @@ public void run() { Log.e(ZeroTierOneService.TAG, e.toString(), e); } } - this.subscriptions = arrayList; + this.subscriptions = groups; Thread.sleep(1000); } catch (InterruptedException e) { Log.d(ZeroTierOneService.TAG, "V4 Multicast Scanner Thread Interrupted", e); + break; } } Log.d(ZeroTierOneService.TAG, "IPv4 Multicast Scanner Thread Ended."); @@ -189,33 +165,17 @@ public void run() { }; private Thread v6MulticastScanner = new Thread() { /* class com.zerotier.one.service.ZeroTierOneService.AnonymousClass2 */ - ArrayList subscriptions = new ArrayList<>(); + List subscriptions = new ArrayList<>(); @Override public void run() { Log.d(ZeroTierOneService.TAG, "IPv6 Multicast Scanner Thread Started."); while (!isInterrupted()) { try { - ArrayList arrayList = new ArrayList<>(); - try { - BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/net/igmp6")); - while (true) { - String readLine = bufferedReader.readLine(); - if (readLine == null) { - break; - } - String[] split = readLine.split("\\s+", -1); - if (split[1].equals("tun0")) { - arrayList.add(split[2]); - } - } - } catch (FileNotFoundException e) { - Log.e(ZeroTierOneService.TAG, "File not found: /proc/net/igmp6", e); - } catch (IOException e) { - Log.e(ZeroTierOneService.TAG, "Error parsing /proc/net/igmp6", e); - } + List groups = NetworkInfoUtils.listMulticastGroupOnInterface("tun0", true); + ArrayList arrayList2 = new ArrayList<>(this.subscriptions); - ArrayList arrayList3 = new ArrayList<>(arrayList); + ArrayList arrayList3 = new ArrayList<>(groups); arrayList3.removeAll(arrayList2); for (String str : arrayList3) { try { @@ -227,7 +187,7 @@ public void run() { Log.e(ZeroTierOneService.TAG, e.toString(), e); } } - arrayList2.removeAll(new ArrayList<>(arrayList)); + arrayList2.removeAll(new ArrayList<>(groups)); for (String str2 : arrayList2) { try { ResultCode multicastUnsubscribe = ZeroTierOneService.this.node.multicastUnsubscribe(ZeroTierOneService.this.networkId, TunTapAdapter.multicastAddressToMAC(InetAddress.getByAddress(StringUtils.hexStringToBytes(str2)))); @@ -238,10 +198,11 @@ public void run() { Log.e(ZeroTierOneService.TAG, e.toString(), e); } } - this.subscriptions = arrayList; + this.subscriptions = groups; Thread.sleep(1000); } catch (InterruptedException e) { Log.d(ZeroTierOneService.TAG, "V6 Multicast Scanner Thread Interrupted", e); + break; } } Log.d(ZeroTierOneService.TAG, "IPv6 Multicast Scanner Thread Ended."); diff --git a/app/src/main/java/net/kaaass/zerotierfix/util/NetworkInfoUtils.java b/app/src/main/java/net/kaaass/zerotierfix/util/NetworkInfoUtils.java index ba1e56e..d283449 100644 --- a/app/src/main/java/net/kaaass/zerotierfix/util/NetworkInfoUtils.java +++ b/app/src/main/java/net/kaaass/zerotierfix/util/NetworkInfoUtils.java @@ -4,11 +4,21 @@ import android.net.ConnectivityManager; import android.net.NetworkCapabilities; import android.os.Build; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; /** * 网络连接信息处理工具类 */ public class NetworkInfoUtils { + public static final String TAG = "NetworkInfoUtils"; + public enum CurrentConnection { CONNECTION_NONE, CONNECTION_MOBILE, @@ -45,4 +55,55 @@ public static CurrentConnection getNetworkInfoCurrentConnection(Context context) return CurrentConnection.CONNECTION_OTHER; } } + + public static List listMulticastGroupOnInterface(String interfaceName, boolean isIpv6) { + var groups = new ArrayList(); + + String igmpFilePath; + + if (isIpv6) { + igmpFilePath = "/proc/net/igmp6"; + } else { + igmpFilePath = "/proc/net/igmp"; + } + + /* + * 从 /proc/net/igmp 或 /proc/net/igmp6 的信息格式大致为: + * + * Idx Device : Count Querier Group Users Timer Reporter + * 1 tun0 : 2 V3 + * 010000E0 1 0:00000000 0 + * 2 wlan0 : 1 V3 + * 010000E0 1 0:00000000 0 + * + * 因此,解析时需要先找到目标 interface 的行,然后开始读取若干行的组播组信息。 + */ + try (var igmpInfo = new BufferedReader(new FileReader(igmpFilePath))) { + boolean foundTargetInterface = false; + String line; + + while ((line = igmpInfo.readLine()) != null) { + var row = line.split("\\s+", -1); + + if (!foundTargetInterface) { + if (row.length > 1 && row[1].equals(interfaceName)) { + // 找到目标 interface 的行 + foundTargetInterface = true; + } + } else { + if (row[0].equals("")) { + groups.add(row[1]); + } else { + // 目标 interface 的信息结束 + foundTargetInterface = false; + } + } + } + } catch (FileNotFoundException e) { + Log.e(TAG, "IGMP info file not found", e); + } catch (IOException e) { + Log.e(TAG, "Error reading IGMP info", e); + } + return groups; + } }