package handlers import ( "fmt" "io" "log" "net/http" "net/url" "os" "regexp" "strings" "github.com/PuerkitoBio/goquery" "github.com/gofiber/fiber/v2" "gopkg.in/yaml.v3" ) var UserAgent = getenv("USER_AGENT", "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)") var ForwardedFor = getenv("X_FORWARDED_FOR", "66.249.66.1") var rulesSet = loadRules() var allowedDomains = strings.Split(os.Getenv("ALLOWED_DOMAINS"), ",") func ProxySite(c *fiber.Ctx) error { // Get the url from the URL url := c.Params("*") queries := c.Queries() body, _, resp, err := fetchSite(url, queries) if err != nil { log.Println("ERROR:", err) c.SendStatus(fiber.StatusInternalServerError) return c.SendString(err.Error()) } c.Set("Content-Type", resp.Header.Get("Content-Type")) c.Set("Content-Security-Policy", resp.Header.Get("Content-Security-Policy")) return c.SendString(body) } func fetchSite(urlpath string, queries map[string]string) (string, *http.Request, *http.Response, error) { urlQuery := "?" if len(queries) > 0 { for k, v := range queries { urlQuery += k + "=" + v + "&" } } urlQuery = strings.TrimSuffix(urlQuery, "&") urlQuery = strings.TrimSuffix(urlQuery, "?") u, err := url.Parse(urlpath) if err != nil { return "", nil, nil, err } if len(allowedDomains) > 0 && !StringInSlice(u.Host, allowedDomains) { return "", nil, nil, fmt.Errorf("domain not allowed. %s not in %s", u.Host, allowedDomains) } if os.Getenv("LOG_URLS") == "true" { log.Println(u.String() + urlQuery) } rule := fetchRule(u.Host, u.Path) if rule.GoogleCache { u, err = url.Parse("https://webcache.googleusercontent.com/search?q=cache:" + u.String()) if err != nil { return "", nil, nil, err } } // Fetch the site client := &http.Client{} req, _ := http.NewRequest("GET", u.String()+urlQuery, nil) if rule.Headers.UserAgent != "" { req.Header.Set("User-Agent", rule.Headers.UserAgent) } else { req.Header.Set("User-Agent", UserAgent) } if rule.Headers.XForwardedFor != "" { if rule.Headers.XForwardedFor != "none" { req.Header.Set("X-Forwarded-For", rule.Headers.XForwardedFor) } } else { req.Header.Set("X-Forwarded-For", ForwardedFor) } if rule.Headers.Referer != "" { if rule.Headers.Referer != "none" { req.Header.Set("Referer", rule.Headers.Referer) } } else { req.Header.Set("Referer", u.String()) } if rule.Headers.Cookie != "" { req.Header.Set("Cookie", rule.Headers.Cookie) } resp, err := client.Do(req) if err != nil { return "", nil, nil, err } defer resp.Body.Close() bodyB, err := io.ReadAll(resp.Body) if err != nil { return "", nil, nil, err } if rule.Headers.CSP != "" { resp.Header.Set("Content-Security-Policy", rule.Headers.CSP) } log.Print("rule", rule) body := rewriteHtml(bodyB, u, rule) return body, req, resp, nil } func rewriteHtml(bodyB []byte, u *url.URL, rule Rule) string { // Rewrite the HTML body := string(bodyB) // images imagePattern := `]*\s+)?src="(/)([^"]*)"` re := regexp.MustCompile(imagePattern) body = re.ReplaceAllString(body, fmt.Sprintf(`]*\s+)?src="(/)([^"]*)"` reScript := regexp.MustCompile(scriptPattern) body = reScript.ReplaceAllString(body, fmt.Sprintf(`