Skip to content

Commit

Permalink
fix: Multicast is wrongly implemented
Browse files Browse the repository at this point in the history
The parsing logic of file `/proc/net/igmp` is wrongly implemented, which will lead to a infinite loop.

This commit should also fix issue #74.
  • Loading branch information
kaaass committed Feb 8, 2024
1 parent b1113dd commit b481035
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 53 deletions.
Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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<String> subscriptions = new ArrayList<>();
List<String> subscriptions = new ArrayList<>();

@Override
public void run() {
Log.d(ZeroTierOneService.TAG, "IPv4 Multicast Scanner Thread Started.");
while (!isInterrupted()) {
try {
ArrayList<String> 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<String> groups = NetworkInfoUtils.listMulticastGroupOnInterface("tun0", false);

ArrayList<String> arrayList2 = new ArrayList<>(this.subscriptions);
ArrayList<String> arrayList3 = new ArrayList<>(arrayList);
ArrayList<String> arrayList3 = new ArrayList<>(groups);
arrayList3.removeAll(arrayList2);
for (String str : arrayList3) {
try {
Expand All @@ -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);
Expand All @@ -178,44 +153,29 @@ 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.");
}
};
private Thread v6MulticastScanner = new Thread() {
/* class com.zerotier.one.service.ZeroTierOneService.AnonymousClass2 */
ArrayList<String> subscriptions = new ArrayList<>();
List<String> subscriptions = new ArrayList<>();

@Override
public void run() {
Log.d(ZeroTierOneService.TAG, "IPv6 Multicast Scanner Thread Started.");
while (!isInterrupted()) {
try {
ArrayList<String> 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<String> groups = NetworkInfoUtils.listMulticastGroupOnInterface("tun0", true);

ArrayList<String> arrayList2 = new ArrayList<>(this.subscriptions);
ArrayList<String> arrayList3 = new ArrayList<>(arrayList);
ArrayList<String> arrayList3 = new ArrayList<>(groups);
arrayList3.removeAll(arrayList2);
for (String str : arrayList3) {
try {
Expand All @@ -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))));
Expand All @@ -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.");
Expand Down
Expand Up @@ -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,
Expand Down Expand Up @@ -45,4 +55,55 @@ public static CurrentConnection getNetworkInfoCurrentConnection(Context context)
return CurrentConnection.CONNECTION_OTHER;
}
}

public static List<String> listMulticastGroupOnInterface(String interfaceName, boolean isIpv6) {
var groups = new ArrayList<String>();

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;
}
}

0 comments on commit b481035

Please sign in to comment.