mirror of
https://github.com/MatsuriDayo/NekoBoxForAndroid.git
synced 2025-12-19 06:30:05 +08:00
feat: generate ruleset
This commit is contained in:
parent
0c3c42aa74
commit
be1edbd5e4
@ -29,6 +29,7 @@ import libcore.Libcore
|
|||||||
import moe.matsuri.nb4a.NativeInterface
|
import moe.matsuri.nb4a.NativeInterface
|
||||||
import moe.matsuri.nb4a.utils.JavaUtil
|
import moe.matsuri.nb4a.utils.JavaUtil
|
||||||
import moe.matsuri.nb4a.utils.cleanWebview
|
import moe.matsuri.nb4a.utils.cleanWebview
|
||||||
|
import java.io.File
|
||||||
import androidx.work.Configuration as WorkConfiguration
|
import androidx.work.Configuration as WorkConfiguration
|
||||||
|
|
||||||
class SagerNet : Application(),
|
class SagerNet : Application(),
|
||||||
@ -40,11 +41,11 @@ class SagerNet : Application(),
|
|||||||
application = this
|
application = this
|
||||||
}
|
}
|
||||||
|
|
||||||
val nativeInterface = NativeInterface()
|
private val nativeInterface = NativeInterface()
|
||||||
|
|
||||||
val externalAssets by lazy { getExternalFilesDir(null) ?: filesDir }
|
val externalAssets: File by lazy { getExternalFilesDir(null) ?: filesDir }
|
||||||
val process = JavaUtil.getProcessName()
|
val process: String = JavaUtil.getProcessName()
|
||||||
val isMainProcess = process == BuildConfig.APPLICATION_ID
|
private val isMainProcess = process == BuildConfig.APPLICATION_ID
|
||||||
val isBgProcess = process.endsWith(":bg")
|
val isBgProcess = process.endsWith(":bg")
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
|||||||
@ -26,11 +26,8 @@ import io.nekohasekai.sagernet.fmt.wireguard.buildSingBoxOutboundWireguardBean
|
|||||||
import io.nekohasekai.sagernet.ktx.isIpAddress
|
import io.nekohasekai.sagernet.ktx.isIpAddress
|
||||||
import io.nekohasekai.sagernet.ktx.mkPort
|
import io.nekohasekai.sagernet.ktx.mkPort
|
||||||
import io.nekohasekai.sagernet.utils.PackageCache
|
import io.nekohasekai.sagernet.utils.PackageCache
|
||||||
import moe.matsuri.nb4a.Protocols
|
import moe.matsuri.nb4a.*
|
||||||
import moe.matsuri.nb4a.SingBoxOptions.*
|
import moe.matsuri.nb4a.SingBoxOptions.*
|
||||||
import moe.matsuri.nb4a.SingBoxOptionsUtil
|
|
||||||
import moe.matsuri.nb4a.checkEmpty
|
|
||||||
import moe.matsuri.nb4a.makeSingBoxRule
|
|
||||||
import moe.matsuri.nb4a.plugin.Plugins
|
import moe.matsuri.nb4a.plugin.Plugins
|
||||||
import moe.matsuri.nb4a.proxy.config.ConfigBean
|
import moe.matsuri.nb4a.proxy.config.ConfigBean
|
||||||
import moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSBean
|
import moe.matsuri.nb4a.proxy.shadowtls.ShadowTLSBean
|
||||||
@ -535,6 +532,9 @@ fun buildConfig(
|
|||||||
if (rule.ip.isNotBlank()) {
|
if (rule.ip.isNotBlank()) {
|
||||||
makeSingBoxRule(rule.ip.listByLineOrComma(), true)
|
makeSingBoxRule(rule.ip.listByLineOrComma(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
generateRuleSet()
|
||||||
|
|
||||||
if (rule.port.isNotBlank()) {
|
if (rule.port.isNotBlank()) {
|
||||||
port = mutableListOf<Int>()
|
port = mutableListOf<Int>()
|
||||||
port_range = mutableListOf<String>()
|
port_range = mutableListOf<String>()
|
||||||
@ -733,7 +733,7 @@ fun buildConfig(
|
|||||||
if (DataStore.bypassLanInCore) {
|
if (DataStore.bypassLanInCore) {
|
||||||
route.rules.add(Rule_DefaultOptions().apply {
|
route.rules.add(Rule_DefaultOptions().apply {
|
||||||
outbound = TAG_BYPASS
|
outbound = TAG_BYPASS
|
||||||
geoip = listOf("private")
|
ip_is_private = true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// block mcast
|
// block mcast
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
package io.nekohasekai.sagernet.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import io.nekohasekai.sagernet.ktx.app
|
||||||
|
import libcore.Libcore
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object GeoipUtils {
|
||||||
|
fun generateRuleSet(context: Context = app.applicationContext, country: String) {
|
||||||
|
|
||||||
|
val filesDir = context.getExternalFilesDir(null) ?: context.filesDir
|
||||||
|
|
||||||
|
val ruleSetDir = filesDir.resolve("ruleSets")
|
||||||
|
ruleSetDir.mkdirs()
|
||||||
|
|
||||||
|
val geositeFile = File(filesDir, "geoip.db")
|
||||||
|
|
||||||
|
val geoip = Libcore.newGeoip()
|
||||||
|
if (!geoip.openGeosite(geositeFile.absolutePath)) {
|
||||||
|
error("open geoip failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
geoip.convertGeoip(country, ruleSetDir.resolve("geoip-$country.srs").absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package io.nekohasekai.sagernet.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import io.nekohasekai.sagernet.ktx.app
|
||||||
|
import libcore.Geosite
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object GeositeUtils {
|
||||||
|
fun generateRuleSet(context: Context = app.applicationContext, code: String) {
|
||||||
|
|
||||||
|
val filesDir = context.getExternalFilesDir(null) ?: context.filesDir
|
||||||
|
|
||||||
|
val ruleSetDir = filesDir.resolve("ruleSets")
|
||||||
|
ruleSetDir.mkdirs()
|
||||||
|
|
||||||
|
val geositeFile = File(filesDir, "geosite.db")
|
||||||
|
|
||||||
|
val geosite = Geosite()
|
||||||
|
if (!geosite.checkGeositeCode(geositeFile.absolutePath, code)) {
|
||||||
|
error("code $code not found in geosite")
|
||||||
|
}
|
||||||
|
|
||||||
|
geosite.convertGeosite(code, ruleSetDir.resolve("geosite-$code.srs").absolutePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -968,12 +968,10 @@ public class SingBoxOptions {
|
|||||||
|
|
||||||
public static class RouteOptions extends SingBoxOption {
|
public static class RouteOptions extends SingBoxOption {
|
||||||
|
|
||||||
public GeoIPOptions geoip;
|
|
||||||
|
|
||||||
public GeositeOptions geosite;
|
|
||||||
|
|
||||||
public List<Rule> rules;
|
public List<Rule> rules;
|
||||||
|
|
||||||
|
public List<RuleSet> rule_set;
|
||||||
|
|
||||||
@SerializedName("final")
|
@SerializedName("final")
|
||||||
public String final_;
|
public String final_;
|
||||||
|
|
||||||
@ -989,26 +987,6 @@ public class SingBoxOptions {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GeoIPOptions extends SingBoxOption {
|
|
||||||
|
|
||||||
public String path;
|
|
||||||
|
|
||||||
public String download_url;
|
|
||||||
|
|
||||||
public String download_detour;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class GeositeOptions extends SingBoxOption {
|
|
||||||
|
|
||||||
public String path;
|
|
||||||
|
|
||||||
public String download_url;
|
|
||||||
|
|
||||||
public String download_detour;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static class Rule extends SingBoxOption {
|
public static class Rule extends SingBoxOption {
|
||||||
|
|
||||||
@ -1020,6 +998,20 @@ public class SingBoxOptions {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class RuleSet extends SingBoxOption {
|
||||||
|
|
||||||
|
public String type;
|
||||||
|
|
||||||
|
public String tag;
|
||||||
|
|
||||||
|
public String format;
|
||||||
|
|
||||||
|
public String path;
|
||||||
|
|
||||||
|
public String url;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static class DefaultRule extends SingBoxOption {
|
public static class DefaultRule extends SingBoxOption {
|
||||||
|
|
||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
@ -1048,15 +1040,6 @@ public class SingBoxOptions {
|
|||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
public List<String> domain_regex;
|
public List<String> domain_regex;
|
||||||
|
|
||||||
// Generate note: Listable
|
|
||||||
public List<String> geosite;
|
|
||||||
|
|
||||||
// Generate note: Listable
|
|
||||||
public List<String> source_geoip;
|
|
||||||
|
|
||||||
// Generate note: Listable
|
|
||||||
public List<String> geoip;
|
|
||||||
|
|
||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
public List<String> source_ip_cidr;
|
public List<String> source_ip_cidr;
|
||||||
|
|
||||||
@ -1098,18 +1081,6 @@ public class SingBoxOptions {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LogicalRule extends SingBoxOption {
|
|
||||||
|
|
||||||
public String mode;
|
|
||||||
|
|
||||||
public List<DefaultRule> rules;
|
|
||||||
|
|
||||||
public Boolean invert;
|
|
||||||
|
|
||||||
public String outbound;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static class DNSRule extends SingBoxOption {
|
public static class DNSRule extends SingBoxOption {
|
||||||
|
|
||||||
@ -1155,9 +1126,6 @@ public class SingBoxOptions {
|
|||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
public List<String> geosite;
|
public List<String> geosite;
|
||||||
|
|
||||||
// Generate note: Listable
|
|
||||||
public List<String> source_geoip;
|
|
||||||
|
|
||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
public List<String> source_ip_cidr;
|
public List<String> source_ip_cidr;
|
||||||
|
|
||||||
@ -1203,22 +1171,6 @@ public class SingBoxOptions {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class LogicalDNSRule extends SingBoxOption {
|
|
||||||
|
|
||||||
public String mode;
|
|
||||||
|
|
||||||
public List<DefaultDNSRule> rules;
|
|
||||||
|
|
||||||
public Boolean invert;
|
|
||||||
|
|
||||||
public String server;
|
|
||||||
|
|
||||||
public Boolean disable_cache;
|
|
||||||
|
|
||||||
public Integer rewrite_ttl;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class ShadowsocksInboundOptions extends SingBoxOption {
|
public static class ShadowsocksInboundOptions extends SingBoxOption {
|
||||||
|
|
||||||
// Generate note: nested type ListenOptions
|
// Generate note: nested type ListenOptions
|
||||||
@ -4387,14 +4339,12 @@ public class SingBoxOptions {
|
|||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
public List<String> domain_regex;
|
public List<String> domain_regex;
|
||||||
|
|
||||||
// Generate note: Listable
|
public List<String> rule_set;
|
||||||
public List<String> geosite;
|
|
||||||
|
|
||||||
// Generate note: Listable
|
public Boolean source_ip_is_private;
|
||||||
public List<String> source_geoip;
|
|
||||||
|
|
||||||
// Generate note: Listable
|
public Boolean rule_set_ipcidr_match_source;
|
||||||
public List<String> geoip;
|
public Boolean ip_is_private;
|
||||||
|
|
||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
public List<String> source_ip_cidr;
|
public List<String> source_ip_cidr;
|
||||||
@ -4437,18 +4387,6 @@ public class SingBoxOptions {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Rule_LogicalOptions extends Rule {
|
|
||||||
|
|
||||||
public String mode;
|
|
||||||
|
|
||||||
public List<DefaultRule> rules;
|
|
||||||
|
|
||||||
public Boolean invert;
|
|
||||||
|
|
||||||
public String outbound;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DNSRule_DefaultOptions extends DNSRule {
|
public static class DNSRule_DefaultOptions extends DNSRule {
|
||||||
|
|
||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
@ -4483,9 +4421,6 @@ public class SingBoxOptions {
|
|||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
public List<String> geosite;
|
public List<String> geosite;
|
||||||
|
|
||||||
// Generate note: Listable
|
|
||||||
public List<String> source_geoip;
|
|
||||||
|
|
||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
public List<String> source_ip_cidr;
|
public List<String> source_ip_cidr;
|
||||||
|
|
||||||
@ -4531,22 +4466,6 @@ public class SingBoxOptions {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DNSRule_LogicalOptions extends DNSRule {
|
|
||||||
|
|
||||||
public String mode;
|
|
||||||
|
|
||||||
public List<DefaultDNSRule> rules;
|
|
||||||
|
|
||||||
public Boolean invert;
|
|
||||||
|
|
||||||
public String server;
|
|
||||||
|
|
||||||
public Boolean disable_cache;
|
|
||||||
|
|
||||||
public Integer rewrite_ttl;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class V2RayTransportOptions_HTTPOptions extends V2RayTransportOptions {
|
public static class V2RayTransportOptions_HTTPOptions extends V2RayTransportOptions {
|
||||||
|
|
||||||
// Generate note: Listable
|
// Generate note: Listable
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package moe.matsuri.nb4a
|
package moe.matsuri.nb4a
|
||||||
|
|
||||||
import io.nekohasekai.sagernet.database.DataStore
|
import io.nekohasekai.sagernet.database.DataStore
|
||||||
|
import io.nekohasekai.sagernet.utils.GeoipUtils
|
||||||
|
import io.nekohasekai.sagernet.utils.GeositeUtils
|
||||||
|
|
||||||
object SingBoxOptionsUtil {
|
object SingBoxOptionsUtil {
|
||||||
|
|
||||||
@ -70,12 +72,26 @@ fun SingBoxOptions.DNSRule_DefaultOptions.checkEmpty(): Boolean {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun SingBoxOptions.Rule_DefaultOptions.generateRuleSet() {
|
||||||
|
rule_set.forEach {
|
||||||
|
when {
|
||||||
|
it.startsWith("geoip") -> {
|
||||||
|
GeoipUtils.generateRuleSet(country = it.removePrefix("geoip:"))
|
||||||
|
}
|
||||||
|
|
||||||
|
it.startsWith("geosite") -> {
|
||||||
|
GeositeUtils.generateRuleSet(code = it.removePrefix("geosite:"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun SingBoxOptions.Rule_DefaultOptions.makeSingBoxRule(list: List<String>, isIP: Boolean) {
|
fun SingBoxOptions.Rule_DefaultOptions.makeSingBoxRule(list: List<String>, isIP: Boolean) {
|
||||||
if (isIP) {
|
if (isIP) {
|
||||||
ip_cidr = mutableListOf<String>()
|
ip_cidr = mutableListOf<String>()
|
||||||
geoip = mutableListOf<String>()
|
rule_set = mutableListOf<String>()
|
||||||
} else {
|
} else {
|
||||||
geosite = mutableListOf<String>()
|
rule_set = mutableListOf<String>()
|
||||||
domain = mutableListOf<String>()
|
domain = mutableListOf<String>()
|
||||||
domain_suffix = mutableListOf<String>()
|
domain_suffix = mutableListOf<String>()
|
||||||
domain_regex = mutableListOf<String>()
|
domain_regex = mutableListOf<String>()
|
||||||
@ -84,14 +100,15 @@ fun SingBoxOptions.Rule_DefaultOptions.makeSingBoxRule(list: List<String>, isIP:
|
|||||||
list.forEach {
|
list.forEach {
|
||||||
if (isIP) {
|
if (isIP) {
|
||||||
if (it.startsWith("geoip:")) {
|
if (it.startsWith("geoip:")) {
|
||||||
geoip.plusAssign(it.removePrefix("geoip:"))
|
rule_set.plusAssign(it)
|
||||||
|
rule_set_ipcidr_match_source = true
|
||||||
} else {
|
} else {
|
||||||
ip_cidr.plusAssign(it)
|
ip_cidr.plusAssign(it)
|
||||||
}
|
}
|
||||||
return@forEach
|
return@forEach
|
||||||
}
|
}
|
||||||
if (it.startsWith("geosite:")) {
|
if (it.startsWith("geosite:")) {
|
||||||
geosite.plusAssign(it.removePrefix("geosite:"))
|
rule_set.plusAssign(it)
|
||||||
} else if (it.startsWith("full:")) {
|
} else if (it.startsWith("full:")) {
|
||||||
domain.plusAssign(it.removePrefix("full:").lowercase())
|
domain.plusAssign(it.removePrefix("full:").lowercase())
|
||||||
} else if (it.startsWith("domain:")) {
|
} else if (it.startsWith("domain:")) {
|
||||||
@ -106,15 +123,12 @@ fun SingBoxOptions.Rule_DefaultOptions.makeSingBoxRule(list: List<String>, isIP:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ip_cidr?.removeIf { it.isNullOrBlank() }
|
ip_cidr?.removeIf { it.isNullOrBlank() }
|
||||||
geoip?.removeIf { it.isNullOrBlank() }
|
rule_set?.removeIf { it.isNullOrBlank() }
|
||||||
geosite?.removeIf { it.isNullOrBlank() }
|
|
||||||
domain?.removeIf { it.isNullOrBlank() }
|
domain?.removeIf { it.isNullOrBlank() }
|
||||||
domain_suffix?.removeIf { it.isNullOrBlank() }
|
domain_suffix?.removeIf { it.isNullOrBlank() }
|
||||||
domain_regex?.removeIf { it.isNullOrBlank() }
|
domain_regex?.removeIf { it.isNullOrBlank() }
|
||||||
domain_keyword?.removeIf { it.isNullOrBlank() }
|
domain_keyword?.removeIf { it.isNullOrBlank() }
|
||||||
if (ip_cidr?.isEmpty() == true) ip_cidr = null
|
if (ip_cidr?.isEmpty() == true) ip_cidr = null
|
||||||
if (geoip?.isEmpty() == true) geoip = null
|
|
||||||
if (geosite?.isEmpty() == true) geosite = null
|
|
||||||
if (domain?.isEmpty() == true) domain = null
|
if (domain?.isEmpty() == true) domain = null
|
||||||
if (domain_suffix?.isEmpty() == true) domain_suffix = null
|
if (domain_suffix?.isEmpty() == true) domain_suffix = null
|
||||||
if (domain_regex?.isEmpty() == true) domain_regex = null
|
if (domain_regex?.isEmpty() == true) domain_regex = null
|
||||||
@ -123,9 +137,8 @@ fun SingBoxOptions.Rule_DefaultOptions.makeSingBoxRule(list: List<String>, isIP:
|
|||||||
|
|
||||||
fun SingBoxOptions.Rule_DefaultOptions.checkEmpty(): Boolean {
|
fun SingBoxOptions.Rule_DefaultOptions.checkEmpty(): Boolean {
|
||||||
if (ip_cidr?.isNotEmpty() == true) return false
|
if (ip_cidr?.isNotEmpty() == true) return false
|
||||||
if (geoip?.isNotEmpty() == true) return false
|
|
||||||
if (geosite?.isNotEmpty() == true) return false
|
|
||||||
if (domain?.isNotEmpty() == true) return false
|
if (domain?.isNotEmpty() == true) return false
|
||||||
|
if (rule_set?.isNotEmpty() == true) return false
|
||||||
if (domain_suffix?.isNotEmpty() == true) return false
|
if (domain_suffix?.isNotEmpty() == true) return false
|
||||||
if (domain_regex?.isNotEmpty() == true) return false
|
if (domain_regex?.isNotEmpty() == true) return false
|
||||||
if (domain_keyword?.isNotEmpty() == true) return false
|
if (domain_keyword?.isNotEmpty() == true) return false
|
||||||
|
|||||||
78
libcore/geoip.go
Normal file
78
libcore/geoip.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package libcore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/oschwald/maxminddb-golang"
|
||||||
|
"github.com/sagernet/sing-box/common/srs"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Geoip struct {
|
||||||
|
geoipReader *maxminddb.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Geoip) OpenGeosite(path string) bool {
|
||||||
|
geoipReader, err := maxminddb.Open(path)
|
||||||
|
g.geoipReader = geoipReader
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to open geoip file:", err)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
log.Println("loaded geoip database")
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Geoip) ConvertGeoip(countryCode, outputPath string) {
|
||||||
|
networks := g.geoipReader.Networks(maxminddb.SkipAliasedNetworks)
|
||||||
|
countryMap := make(map[string][]*net.IPNet)
|
||||||
|
var (
|
||||||
|
ipNet *net.IPNet
|
||||||
|
nextCountryCode string
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
for networks.Next() {
|
||||||
|
ipNet, err = networks.Network(&nextCountryCode)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to get network:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
countryMap[nextCountryCode] = append(countryMap[nextCountryCode], ipNet)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipNets := countryMap[strings.ToLower(countryCode)]
|
||||||
|
|
||||||
|
if len(ipNets) == 0 {
|
||||||
|
log.Println("no networks found for country code:", countryCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var headlessRule option.DefaultHeadlessRule
|
||||||
|
headlessRule.IPCIDR = make([]string, 0, len(ipNets))
|
||||||
|
for _, cidr := range ipNets {
|
||||||
|
headlessRule.IPCIDR = append(headlessRule.IPCIDR, cidr.String())
|
||||||
|
}
|
||||||
|
var plainRuleSet option.PlainRuleSetCompat
|
||||||
|
plainRuleSet.Version = C.RuleSetVersion1
|
||||||
|
plainRuleSet.Options.Rules = []option.HeadlessRule{
|
||||||
|
{
|
||||||
|
Type: C.RuleTypeDefault,
|
||||||
|
DefaultOptions: headlessRule,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFile, err := os.Create(outputPath)
|
||||||
|
err = srs.Write(outputFile, plainRuleSet.Upgrade())
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to write geosite file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGeoip() *Geoip {
|
||||||
|
return new(Geoip)
|
||||||
|
}
|
||||||
70
libcore/geosite.go
Normal file
70
libcore/geosite.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package libcore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sagernet/sing-box/common/geosite"
|
||||||
|
"github.com/sagernet/sing-box/common/srs"
|
||||||
|
C "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/option"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"log"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Geosite struct {
|
||||||
|
geositeReader *geosite.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Geosite) CheckGeositeCode(path string, code string) bool {
|
||||||
|
geositeReader, codes, err := geosite.Open(path)
|
||||||
|
g.geositeReader = geositeReader
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to open geosite file:", err)
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
log.Println("loaded geosite database: ", len(codes), " codes")
|
||||||
|
}
|
||||||
|
sourceSet, err := geositeReader.Read(code)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to read geosite code:", code, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(sourceSet) >= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConvertGeosite need to run CheckGeositeCode first
|
||||||
|
func (g *Geosite) ConvertGeosite(code string, outputPath string) {
|
||||||
|
|
||||||
|
sourceSet, err := g.geositeReader.Read(code)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to read geosite code:", code, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var headlessRule option.DefaultHeadlessRule
|
||||||
|
|
||||||
|
defaultRule := geosite.Compile(sourceSet)
|
||||||
|
|
||||||
|
headlessRule.Domain = defaultRule.Domain
|
||||||
|
headlessRule.DomainSuffix = defaultRule.DomainSuffix
|
||||||
|
headlessRule.DomainKeyword = defaultRule.DomainKeyword
|
||||||
|
headlessRule.DomainRegex = defaultRule.DomainRegex
|
||||||
|
var plainRuleSet option.PlainRuleSetCompat
|
||||||
|
plainRuleSet.Version = C.RuleSetVersion1
|
||||||
|
plainRuleSet.Options.Rules = []option.HeadlessRule{
|
||||||
|
{
|
||||||
|
Type: C.RuleTypeDefault,
|
||||||
|
DefaultOptions: headlessRule,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFile, err := os.Create(outputPath)
|
||||||
|
err = srs.Write(outputFile, plainRuleSet.Upgrade())
|
||||||
|
if err != nil {
|
||||||
|
log.Println("failed to write geosite file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newGeosite() *Geosite {
|
||||||
|
return new(Geosite)
|
||||||
|
}
|
||||||
@ -14,6 +14,8 @@ require (
|
|||||||
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da
|
golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da
|
||||||
)
|
)
|
||||||
|
|
||||||
|
require github.com/oschwald/maxminddb-golang v1.12.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
berty.tech/go-libtor v1.0.385 // indirect
|
berty.tech/go-libtor v1.0.385 // indirect
|
||||||
github.com/ajg/form v1.5.1 // indirect
|
github.com/ajg/form v1.5.1 // indirect
|
||||||
@ -48,7 +50,6 @@ require (
|
|||||||
github.com/mholt/acmez v1.2.0 // indirect
|
github.com/mholt/acmez v1.2.0 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
github.com/onsi/ginkgo/v2 v2.9.7 // indirect
|
||||||
github.com/ooni/go-libtor v1.1.8 // indirect
|
github.com/ooni/go-libtor v1.1.8 // indirect
|
||||||
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
|
|
||||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||||
github.com/quic-go/qpack v0.4.0 // indirect
|
github.com/quic-go/qpack v0.4.0 // indirect
|
||||||
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
github.com/quic-go/qtls-go1-20 v0.4.1 // indirect
|
||||||
|
|||||||
@ -6,8 +6,6 @@ github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sx
|
|||||||
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
github.com/caddyserver/certmagic v0.20.0 h1:bTw7LcEZAh9ucYCRXyCpIrSAGplplI0vGYJ4BpCQ/Fc=
|
||||||
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
github.com/caddyserver/certmagic v0.20.0/go.mod h1:N4sXgpICQUskEWpj7zVzvWD41p3NYacrNoZYiRM2jTg=
|
||||||
github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg=
|
|
||||||
github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
|
||||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user