package main import ( "bytes" "compress/gzip" "fmt" "io" "net/http" "net/http/httputil" "net/url" "os" "regexp" "strings" ) const MAX_SIZE = 2 ^ 16 // 65kb func main() { proxy := &httputil.ReverseProxy{ Rewrite: func(r *httputil.ProxyRequest) { host, _ := Yadro2Kernel(r.In.Host, true) url, _ := url.Parse("https://" + host) r.SetURL(url) // We only support gzip decoding r.Out.Header.Set("Accept-Encoding", "gzip") r.Out.Header.Set("User-Agent", "ядро.орг/1.0") }, ModifyResponse: func(r *http.Response) error { // Disable security policy because of the domain restrictions. r.Header.Del("Content-Security-Policy") // Skip non-html pages. contentType := r.Header.Get("Content-Type") if !strings.HasPrefix(contentType, "text/") { return nil } r.Header.Set("Content-Type", contentType+";charset=utf-8") // Read response body into data. If body is encoded, decode it. var data []byte switch r.Header.Get("Content-Encoding") { case "gzip": reader, _ := gzip.NewReader(r.Body) data, _ = io.ReadAll(io.LimitReader(reader, MAX_SIZE)) reader.Close() default: data, _ = io.ReadAll(io.LimitReader(r.Body, MAX_SIZE)) } r.Body.Close() // Rewrite 30x redirect location locHeader := r.Header.Get("Location") if locHeader != "" { re := regexp.MustCompile(`https?:\/\/[A-Za-z\-\.]*.?kernel\.org\/`) r.Header.Set("Location", string(re.ReplaceAllFunc([]byte(locHeader), func(original_raw []byte) []byte { kernel := strings.ToLower(string(original_raw)) kernel = strings.TrimPrefix(kernel, "https://") kernel = strings.TrimPrefix(kernel, "http://") kernel = strings.TrimPrefix(kernel, "www.") kernel = strings.TrimSuffix(kernel, "/") yadro, exists := Kernel2Yadro(kernel) if !exists { fmt.Println("Missing:", kernel) return original_raw } return []byte("https://" + yadro + "/") }))) } // Modify the response. data = modifyResponse(data) // Remove headers that mess with body encoding and set the body. r.Header.Del("Content-Encoding") r.Header.Del("Content-Length") r.Body = io.NopCloser(bytes.NewReader(data)) return nil }, } http.ListenAndServe("0.0.0.0:80", proxy) } func replaceDomains(response string) string { re := regexp.MustCompile(`(?i)[A-Za-z\-\.]*\.?kernel\.org`) response = strings.NewReplacer( "%3F", "?", "%26", "&", ).Replace(response) response = re.ReplaceAllStringFunc(response, func(original_raw string) string { kernel := strings.ToLower(original_raw) // Strip `www.` kernel = strings.TrimPrefix(kernel, "www.") yadro, exists := Kernel2Yadro(kernel) if !exists { fmt.Println("Missing:", kernel) return original_raw } return yadro }) return response } func translateWithPromtPuppies(response string) string { // Don't try to translate empty body (30x, etc) if len(response) == 0 { return response } req, _ := http.NewRequest("POST", "http://caddy:9000/translate", strings.NewReader(response)) req.Header.Add("Content-Type", "text/html") resp, err := http.DefaultClient.Do(req) if err != nil { fmt.Fprintln(os.Stderr, "Error in while translating", err) return "" } var translated strings.Builder io.Copy(&translated, resp.Body) resp.Body.Close() return translated.String() } func enfunnify(response string) string { return strings.NewReplacer( "tarball", "tar ball", ).Replace(response) } func modifyResponse(response []byte) []byte { s := string(response) s = enfunnify(s) s = translateWithPromtPuppies(s) s = replaceDomains(s) return []byte(s) }