From 1bd203c5555046b7ee4fbfe2f822eb3d03571ad7 Mon Sep 17 00:00:00 2001 From: SergeiEU <39683682+SergeiEU@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:17:15 +0400 Subject: Initial import --- internal/config/builder.go | 182 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 internal/config/builder.go (limited to 'internal/config/builder.go') 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, + } + } +} -- cgit v1.2.3