package main import ( "encoding/json" "log" "os" "path/filepath" "strings" "sync" ) // proxyRule is the minimal structure we need from Zoraxy's proxy config files. type proxyRule struct { Domain string `json:"RootOrMatchingDomain"` ActiveOrigins []struct { Origin string `json:"OriginIpOrDomain"` } `json:"ActiveOrigins"` Disabled bool `json:"Disabled"` } // hostMap maps request Host headers to backend URLs. var ( hostMap = make(map[string]string) hostMapMu sync.RWMutex ) // loadHostMap reads Zoraxy proxy configs and builds a host→backend map. func loadHostMap() { hostMapMu.Lock() defer hostMapMu.Unlock() hostMap = make(map[string]string) proxyDir := "/opt/zoraxy/conf/proxy" entries, err := os.ReadDir(proxyDir) if err != nil { log.Printf("WARN: cannot read proxy configs: %v", err) return } for _, entry := range entries { if !strings.HasSuffix(entry.Name(), ".config") { continue } path := filepath.Join(proxyDir, entry.Name()) data, err := os.ReadFile(path) if err != nil { continue } var rule proxyRule if err := json.Unmarshal(data, &rule); err != nil { continue } if rule.Disabled || len(rule.ActiveOrigins) == 0 { continue } hostMap[strings.ToLower(rule.Domain)] = rule.ActiveOrigins[0].Origin } log.Printf("Firewall: loaded %d proxy backends", len(hostMap)) } // resolveBackend finds the backend URL for a given Host header. // Falls back to the configured BackendURL if no match. func resolveBackend(host string) string { hostMapMu.RLock() defer hostMapMu.RUnlock() if backend, ok := hostMap[strings.ToLower(host)]; ok { return backend } if backend, ok := hostMap[strings.ToLower(strings.Split(host, ":")[0])]; ok { return backend } // Fallback return getConfig().BackendURL }