summaryrefslogtreecommitdiff
path: root/internal/config/builder.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/config/builder.go')
-rw-r--r--internal/config/builder.go182
1 files changed, 182 insertions, 0 deletions
diff --git a/internal/config/builder.go b/internal/config/builder.go
new file mode 100644
index 0000000..a9e513b
--- /dev/null
+++ b/internal/config/builder.go
@@ -0,0 +1,182 @@
+package config
+
+import (
+ "vpnem/internal/models"
+)
+
+type SingBoxConfig struct {
+ DNS map[string]any `json:"dns"`
+ Inbounds []map[string]any `json:"inbounds"`
+ Outbounds []map[string]any `json:"outbounds"`
+ Route map[string]any `json:"route"`
+ Experimental map[string]any `json:"experimental,omitempty"`
+}
+
+var blockedDomains = []string{
+ // Telegram
+ "telegram.org", "t.me", "telegram.me", "telegra.ph", "telegram.dog",
+ "web.telegram.org",
+ // Discord
+ "discord.com", "discord.gg", "discordapp.com", "discordapp.net",
+ // Meta
+ "instagram.com", "cdninstagram.com", "ig.me", "igcdn.com",
+ "facebook.com", "fb.com", "fbcdn.net", "fbsbx.com", "fb.me",
+ "whatsapp.com", "whatsapp.net",
+ // Twitter/X
+ "twitter.com", "x.com", "twimg.com", "t.co",
+ // AI
+ "openai.com", "chatgpt.com", "oaistatic.com", "oaiusercontent.com",
+ "claude.ai", "anthropic.com",
+ // YouTube/Google
+ "youtube.com", "googlevideo.com", "youtu.be", "ggpht.com", "ytimg.com",
+ "gstatic.com", "doubleclick.net", "googleadservices.com",
+ // Cam sites
+ "stripchat.com", "stripchat.global", "ststandard.com", "strpssts-ana.com",
+ "strpst.com", "striiiipst.com",
+ "chaturbate.com", "highwebmedia.com", "cb.dev",
+ "camsoda.com", "cam4.com", "cam101.com",
+ "bongamodels.com", "flirt4free.com", "privatecams.com",
+ "streamray.com", "cams.com", "homelivesex.com",
+ "skyprivate.com", "mywebcamroom.com", "livemediahost.com",
+ // Cam CDNs
+ "xcdnpro.com", "mmcdn.com", "vscdns.com", "bgicdn.com", "bgmicdn.com",
+ "doppiocdn.com", "doppiocdn.net", "doppiostreams.com",
+ "fanclubs.tech", "my.club", "chapturist.com",
+ // Cam analytics/services
+ "moengage.com", "amplitude.com", "dwin1.com",
+ "eizzih.com", "loo3laej.com", "iesnare.com",
+ "hytto.com", "zendesk.com",
+ // Lovense
+ "lovense.com", "lovense-api.com", "lovense.club",
+ // Bitrix
+ "bitrix24.ru", "bitrix24.com",
+ // Cloudflare
+ "cloudflare.com",
+ // Other blocked
+ "viber.com", "linkedin.com", "spotify.com",
+ "ntc.party", "ipify.org",
+ "rutracker.org", "rutracker.net", "rutracker.me",
+ "4pda.to", "kinozal.tv", "nnmclub.to",
+ "protonmail.com", "proton.me", "tutanota.com",
+ "medium.com", "archive.org", "soundcloud.com", "twitch.tv",
+ // IP check
+ "ifconfig.me", "ifconfig.co", "icanhazip.com", "ipinfo.io",
+ // Email
+ "em-mail.ru",
+}
+
+func BuildConfig(server models.Server, mode Mode, ruleSets []models.RuleSet, serverIPs []string) SingBoxConfig {
+ return BuildConfigFull(server, mode, ruleSets, serverIPs, nil)
+}
+
+// BuildConfigFull — exact vpn.py config. Fast, proven.
+func BuildConfigFull(server models.Server, mode Mode, ruleSets []models.RuleSet, serverIPs []string, customBypass []string) SingBoxConfig {
+ bypassIPs := BuildBypassIPs(serverIPs)
+ bypassProcs := BuildBypassProcesses(customBypass)
+
+ var rules []map[string]any
+ rules = append(rules, map[string]any{"ip_cidr": bypassIPs, "outbound": "direct"})
+ rules = append(rules, map[string]any{"process_name": bypassProcs, "outbound": "direct"})
+ rules = append(rules, map[string]any{"domain_suffix": LocalDomainSuffixes, "outbound": "direct"})
+ // Bypass own infrastructure — prevent double proxying when OmegaSwitcher is also active
+ rules = append(rules, map[string]any{"domain_suffix": []string{"em-sysadmin.xyz"}, "outbound": "direct"})
+ rules = append(rules, map[string]any{"process_path_regex": LovenseProcessRegex, "outbound": "proxy"})
+ rules = append(rules, map[string]any{"ip_cidr": ForcedProxyIPs, "outbound": "proxy"})
+ rules = append(rules, map[string]any{"domain_suffix": TelegramDomains, "outbound": "proxy"})
+ rules = append(rules, map[string]any{"domain_regex": TelegramDomainRegex, "outbound": "proxy"})
+ rules = append(rules, map[string]any{"ip_cidr": TelegramIPs, "outbound": "proxy"})
+ rules = append(rules, map[string]any{"domain_suffix": blockedDomains, "outbound": "proxy"})
+
+ for _, r := range mode.Rules {
+ rule := map[string]any{"outbound": r.Outbound}
+ if len(r.DomainSuffix) > 0 { rule["domain_suffix"] = r.DomainSuffix }
+ if len(r.DomainRegex) > 0 { rule["domain_regex"] = r.DomainRegex }
+ if len(r.IPCIDR) > 0 { rule["ip_cidr"] = r.IPCIDR }
+ if len(r.RuleSet) > 0 { rule["rule_set"] = r.RuleSet }
+ if len(r.Network) > 0 { rule["network"] = r.Network }
+ if len(r.PortRange) > 0 { rule["port_range"] = r.PortRange }
+ rules = append(rules, rule)
+ }
+
+ var ruleSetDefs []map[string]any
+ for _, rs := range ruleSets {
+ if rs.URL == "" { continue }
+ ruleSetDefs = append(ruleSetDefs, map[string]any{
+ "tag": rs.Tag, "type": "remote", "format": rs.Format,
+ "url": rs.URL, "download_detour": "direct", "update_interval": "1d",
+ })
+ }
+
+ route := map[string]any{
+ "auto_detect_interface": true,
+ "final": mode.Final,
+ "rules": rules,
+ }
+ if len(ruleSetDefs) > 0 {
+ route["rule_set"] = ruleSetDefs
+ }
+
+ return SingBoxConfig{
+ DNS: map[string]any{
+ "servers": []map[string]any{
+ {"tag": "proxy-dns", "address": "https://8.8.8.8/dns-query", "detour": "proxy"},
+ {"tag": "direct-dns", "address": "https://1.1.1.1/dns-query", "detour": "direct"},
+ },
+ "rules": []map[string]any{
+ {"outbound": "proxy", "server": "proxy-dns"},
+ {"outbound": "direct", "server": "direct-dns"},
+ },
+ "strategy": "ipv4_only",
+ },
+ Inbounds: []map[string]any{
+ {
+ "type": "tun",
+ "interface_name": "singbox",
+ "address": []string{"172.19.0.1/30"},
+ "auto_route": true,
+ "strict_route": false,
+ "stack": "gvisor",
+ "sniff": true,
+ "sniff_override_destination": true,
+ },
+ },
+ Outbounds: []map[string]any{
+ buildOutbound(server),
+ {"type": "direct", "tag": "direct"},
+ },
+ Route: route,
+ Experimental: map[string]any{
+ "cache_file": map[string]any{
+ "enabled": true,
+ "path": "cache.db",
+ },
+ },
+ }
+}
+
+
+
+func buildOutbound(s models.Server) map[string]any {
+ switch s.Type {
+ case "vless":
+ out := map[string]any{
+ "type": "vless", "tag": "proxy",
+ "server": s.Server, "server_port": s.ServerPort, "uuid": s.UUID,
+ }
+ if s.TLS != nil { out["tls"] = map[string]any{"enabled": s.TLS.Enabled, "server_name": s.TLS.ServerName} }
+ if s.Transport != nil { out["transport"] = map[string]any{"type": s.Transport.Type, "path": s.Transport.Path} }
+ return out
+ case "shadowsocks":
+ return map[string]any{
+ "type": "shadowsocks", "tag": "proxy",
+ "server": s.Server, "server_port": s.ServerPort,
+ "method": s.Method, "password": s.Password,
+ }
+ default:
+ return map[string]any{
+ "type": "socks", "tag": "proxy",
+ "server": s.Server, "server_port": s.ServerPort,
+ "udp_over_tcp": s.UDPOverTCP,
+ }
+ }
+}