Skip to content

Commit 611f9bd

Browse files
committed
Add support for MAC and hostname rule items
1 parent b526094 commit 611f9bd

8 files changed

Lines changed: 344 additions & 2 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.nekohasekai.sfa.bg;
2+
3+
import io.nekohasekai.sfa.bg.ParceledListSlice;
4+
5+
interface INeighborTableCallback {
6+
oneway void onNeighborTableUpdated(in ParceledListSlice entries);
7+
}

app/src/main/aidl/io/nekohasekai/sfa/bg/IRootService.aidl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.nekohasekai.sfa.bg;
22

33
import android.os.ParcelFileDescriptor;
4+
import io.nekohasekai.sfa.bg.INeighborTableCallback;
45
import io.nekohasekai.sfa.bg.ParceledListSlice;
56

67
interface IRootService {
@@ -11,4 +12,8 @@ interface IRootService {
1112
void installPackage(in ParcelFileDescriptor apk, long size, int userId) = 2;
1213

1314
String exportDebugInfo(String outputPath) = 3;
15+
16+
void registerNeighborTableCallback(in INeighborTableCallback callback) = 4;
17+
18+
oneway void unregisterNeighborTableCallback(in INeighborTableCallback callback) = 5;
1419
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package io.nekohasekai.sfa.bg;
2+
3+
parcelable NeighborEntry;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package io.nekohasekai.sfa.bg;
2+
3+
import android.os.Parcel;
4+
import android.os.Parcelable;
5+
import androidx.annotation.NonNull;
6+
7+
public class NeighborEntry implements Parcelable {
8+
@NonNull public final String address;
9+
@NonNull public final String macAddress;
10+
@NonNull public final String hostname;
11+
12+
public NeighborEntry(@NonNull String address, @NonNull String macAddress, @NonNull String hostname) {
13+
this.address = address;
14+
this.macAddress = macAddress;
15+
this.hostname = hostname;
16+
}
17+
18+
protected NeighborEntry(Parcel in) {
19+
address = in.readString();
20+
macAddress = in.readString();
21+
hostname = in.readString();
22+
}
23+
24+
@Override
25+
public void writeToParcel(@NonNull Parcel dest, int flags) {
26+
dest.writeString(address);
27+
dest.writeString(macAddress);
28+
dest.writeString(hostname);
29+
}
30+
31+
@Override
32+
public int describeContents() {
33+
return 0;
34+
}
35+
36+
public static final Creator<NeighborEntry> CREATOR =
37+
new Creator<>() {
38+
@Override
39+
public NeighborEntry createFromParcel(Parcel in) {
40+
return new NeighborEntry(in);
41+
}
42+
43+
@Override
44+
public NeighborEntry[] newArray(int size) {
45+
return new NeighborEntry[size];
46+
}
47+
};
48+
}

app/src/main/java/io/nekohasekai/sfa/bg/ParceledListSlice.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
136136
new Parcelable.ClassLoaderCreator<ParceledListSlice>() {
137137
@Override
138138
public ParceledListSlice createFromParcel(Parcel in) {
139-
return new ParceledListSlice(in, null);
139+
return new ParceledListSlice(in, ParceledListSlice.class.getClassLoader());
140140
}
141141

142142
@Override

app/src/main/java/io/nekohasekai/sfa/bg/PlatformInterfaceWrapper.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,28 @@ import io.nekohasekai.libbox.ConnectionOwner
1111
import io.nekohasekai.libbox.InterfaceUpdateListener
1212
import io.nekohasekai.libbox.Libbox
1313
import io.nekohasekai.libbox.LocalDNSTransport
14+
import io.nekohasekai.libbox.NeighborEntryIterator
15+
import io.nekohasekai.libbox.NeighborUpdateListener
1416
import io.nekohasekai.libbox.NetworkInterfaceIterator
1517
import io.nekohasekai.libbox.PlatformInterface
1618
import io.nekohasekai.libbox.StringIterator
1719
import io.nekohasekai.libbox.TunOptions
1820
import io.nekohasekai.libbox.WIFIState
1921
import io.nekohasekai.sfa.Application
22+
import kotlinx.coroutines.Dispatchers
23+
import kotlinx.coroutines.runBlocking
2024
import java.net.Inet6Address
2125
import java.net.InetSocketAddress
2226
import java.net.InterfaceAddress
2327
import java.net.NetworkInterface
2428
import java.security.KeyStore
2529
import kotlin.io.encoding.Base64
2630
import kotlin.io.encoding.ExperimentalEncodingApi
31+
import io.nekohasekai.libbox.NeighborEntry as LibboxNeighborEntry
2732
import io.nekohasekai.libbox.NetworkInterface as LibboxNetworkInterface
2833

34+
private var neighborCallback: INeighborTableCallback.Stub? = null
35+
2936
interface PlatformInterfaceWrapper : PlatformInterface {
3037
override fun usePlatformAutoDetectInterfaceControl(): Boolean = true
3138

@@ -172,6 +179,45 @@ interface PlatformInterfaceWrapper : PlatformInterface {
172179
return StringArray(certificates.iterator())
173180
}
174181

182+
override fun startNeighborMonitor(listener: NeighborUpdateListener?) {
183+
if (listener == null) return
184+
val callback = object : INeighborTableCallback.Stub() {
185+
override fun onNeighborTableUpdated(entries: ParceledListSlice<*>?) {
186+
if (entries == null) return
187+
@Suppress("UNCHECKED_CAST")
188+
val list = entries.list as List<NeighborEntry>
189+
listener.updateNeighborTable(NeighborEntryArray(list.map { entry ->
190+
LibboxNeighborEntry().apply {
191+
address = entry.address
192+
macAddress = entry.macAddress
193+
hostname = entry.hostname
194+
}
195+
}.iterator()))
196+
}
197+
}
198+
neighborCallback = callback
199+
runBlocking(Dispatchers.IO) {
200+
RootClient.registerNeighborTableCallback(callback)
201+
}
202+
}
203+
204+
override fun registerMyInterface(name: String?) {
205+
}
206+
207+
override fun closeNeighborMonitor(listener: NeighborUpdateListener?) {
208+
val callback = neighborCallback ?: return
209+
neighborCallback = null
210+
runBlocking(Dispatchers.IO) {
211+
RootClient.unregisterNeighborTableCallback(callback)
212+
}
213+
}
214+
215+
private class NeighborEntryArray(private val iterator: Iterator<LibboxNeighborEntry>) : NeighborEntryIterator {
216+
override fun hasNext(): Boolean = iterator.hasNext()
217+
218+
override fun next(): LibboxNeighborEntry = iterator.next()
219+
}
220+
175221
private class InterfaceArray(private val iterator: Iterator<LibboxNetworkInterface>) : NetworkInterfaceIterator {
176222
override fun hasNext(): Boolean = iterator.hasNext()
177223

app/src/main/java/io/nekohasekai/sfa/bg/RootClient.kt

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import android.content.ServiceConnection
66
import android.content.pm.PackageInfo
77
import android.os.IBinder
88
import android.os.RemoteException
9+
import androidx.core.content.ContextCompat
910
import com.topjohnwu.superuser.Shell
1011
import com.topjohnwu.superuser.ipc.RootService
1112
import io.nekohasekai.sfa.Application
@@ -17,7 +18,9 @@ import kotlinx.coroutines.suspendCancellableCoroutine
1718
import kotlinx.coroutines.sync.Mutex
1819
import kotlinx.coroutines.sync.withLock
1920
import kotlinx.coroutines.withContext
21+
import java.io.IOException
2022
import kotlin.coroutines.resume
23+
import kotlin.coroutines.resumeWithException
2124

2225
object RootClient {
2326
init {
@@ -53,6 +56,10 @@ object RootClient {
5356
suspend fun bindService(): IRootService = connectionMutex.withLock {
5457
service?.let { return it }
5558

59+
if (Shell.isAppGrantedRoot() == false) {
60+
throw IOException("permission denied")
61+
}
62+
5663
return withContext(Dispatchers.Main) {
5764
suspendCancellableCoroutine { continuation ->
5865
val conn = object : ServiceConnection {
@@ -72,7 +79,30 @@ object RootClient {
7279
}
7380

7481
val intent = Intent(Application.application, RootServer::class.java)
75-
RootService.bind(intent, conn)
82+
val task = RootService.bindOrTask(
83+
intent,
84+
ContextCompat.getMainExecutor(Application.application),
85+
conn,
86+
)
87+
88+
if (task == null) {
89+
// Already connected, onServiceConnected will fire
90+
} else {
91+
Shell.EXECUTOR.execute {
92+
try {
93+
val shell = Shell.getShell()
94+
if (shell.isRoot) {
95+
shell.execTask(task)
96+
} else {
97+
continuation.resumeWithException(
98+
IOException("permission denied")
99+
)
100+
}
101+
} catch (e: Exception) {
102+
continuation.resumeWithException(e)
103+
}
104+
}
105+
}
76106

77107
continuation.invokeOnCancellation {
78108
RootService.unbind(conn)
@@ -103,4 +133,21 @@ object RootClient {
103133
throw e.rethrowFromSystemServer()
104134
}
105135
}
136+
137+
suspend fun registerNeighborTableCallback(callback: INeighborTableCallback) {
138+
val svc = bindService()
139+
try {
140+
svc.registerNeighborTableCallback(callback)
141+
} catch (e: RemoteException) {
142+
throw e.rethrowFromSystemServer()
143+
}
144+
}
145+
146+
suspend fun unregisterNeighborTableCallback(callback: INeighborTableCallback) {
147+
try {
148+
service?.unregisterNeighborTableCallback(callback)
149+
} catch (e: RemoteException) {
150+
throw e.rethrowFromSystemServer()
151+
}
152+
}
106153
}

0 commit comments

Comments
 (0)