func (h *FilteringHandler) OnRequest(r *http.Request, ctx *goproxy.ProxyCtx) ( *http.Request, *http.Response) { host := r.URL.Host if host == "" { host = r.Host } rq := &adblock.Request{ URL: r.URL.String(), Domain: host, OriginDomain: getReferrerDomain(r), } rules := h.Cache.Rules() start := time.Now() matched, id := rules.Matcher.Match(rq) end := time.Now() duration := end.Sub(start) / time.Millisecond if matched { rule := rules.Rules[id] log.Printf("rejected in %dms: %s\n", duration, r.URL.String()) log.Printf(" by %s\n", rule) return r, goproxy.NewResponse(r, goproxy.ContentTypeText, http.StatusNotFound, "Not Found") } ctx.UserData = &ProxyState{ Duration: duration, URL: r.URL.String(), } return r, nil }
func (c *TLSConfigCache) GetConfig(host string, ctx *goproxy.ProxyCtx) (*tls.Config, error) { host = getWildcardHost(host) c.lock.Lock() cached, ok := c.cache[host] if !ok { // Register a config generation event cached = CachedConfig{ Ready: make(chan struct{}), } c.cache[host] = cached } if ok { c.hit += 1 } else { c.miss += 1 } hit := c.hit miss := c.miss c.lock.Unlock() ctx.Warnf("signing hit/miss: %d/%d (%.1f%%)", hit, miss, 100.0*float64(hit)/float64(hit+miss)) if ok { // config is being generated or is ready, grab it <-cached.Ready cfg := cached.Config if cfg == nil { return nil, fmt.Errorf("failed to generate TLS config for %s", host) } return cfg, nil } // Generate it start := time.Now() cfg, err := c.cfgBuilder(host, ctx) stop := time.Now() ctx.Warnf("signing %s in %.0fms", host, float64(stop.Sub(start))/float64(time.Millisecond)) c.lock.Lock() if err == nil { c.cache[host] = CachedConfig{ Config: cfg, Ready: cached.Ready, } } else { delete(c.cache, host) ctx.Warnf("failed to sign %s: %s", host, err) } close(cached.Ready) c.lock.Unlock() return cfg, err }
// fetchRequest fetches the original users' request. // adds client certificate to authenticate func fetchRequest(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Response, error) { // clean up the recycled header we got from the user ctx.Logf("OldRequestURI is %s", req.RequestURI) req.RequestURI = "" client, err := makeClient(req.URL.Host) if err != nil { return nil, err } ctx.Logf("Connecting to: %s", req.URL.String()) resp, err := client.Do(req) if err != nil { return nil, err } // The rest of this function should go in a separate Response handler.. // test if we need to authenticate. if resp.StatusCode != 401 { // nope, we're done. Exit here. return resp, nil } ctx.Logf("status code is 401") // We have an 401-authorization failed // Test for a WWW-Authenticate: Ecca .... header. auth := ParseWWWAuthHeader(resp.Header.Get("Www-Authenticate")) if auth == nil { // No Ecca-authentication required. We're done. Exit here. log.Printf("No WWW-Authenticate: Ecca header, sending 401 response to client") return resp, nil } ctx.Logf("WWW-Authenticate: Ecca header found: %#v", auth) // remember registerURL for the signup-phase registerURLmap[req.Host] = auth["register"] // redirect to Ecca-Proxy user agent (ourself) to log in or sign up resp.Body.Close() resp = redirectToSelector(req) return resp, nil }
// eccaProxy: proxy the user requests and authenticate with the credentials we know. func eccaProxy(req *http.Request, ctx *goproxy.ProxyCtx) (*http.Request, *http.Response) { //log.Println("\n\n\nRequest is ", req.Method, req.URL.String()) ctx.Logf("Start-of-eccaProxy handler") for _, c := range req.Cookies() { ctx.Logf("Cookie send by the client is: %#v", c.Name) } // set the scheme to https so we connect upstream securely if req.URL.Scheme == "http" { req.URL.Scheme = "https" } // Copy the body because we need it untouched. But we also need to parse // the POST parameters and that eats the original buffer with the body body, err := ioutil.ReadAll(req.Body) check(err) req.Body.Close() // close it before replacing. Prevents leaking file descriptors. // give the data back immedeately. req.Body = ioutil.NopCloser(bytes.NewReader(body)) // Read the parameters req.ParseForm() // eats req.Body req.Body = ioutil.NopCloser(bytes.NewReader(body)) // copy back in again // Check for POST method with 'encrypt', 'sign' or 'initiate-direct-connection' parameter. if req.Method == "POST" { if req.Form.Get("initiate-direct-connection") == "required" { // create a direct connection listener, awaiting reply return initiateDirectConnection(req) } else if req.Form.Get("encrypt") == "required" { // transparantly encrypt and sign for a private message return encryptMessage(req) } else if req.Form.Get("sign") == "required" || req.Form.Get("sign") == "optional" { // transparently sign the message before publication return signMessage(req, ctx) } } // Fetch the request from upstream resp, err := fetchRequest(req, ctx) if err != nil { ctx.Warnf("There was an error fetching the users' request: %v", err) return req, goproxy.NewResponse(req, goproxy.ContentTypeText, http.StatusInternalServerError, "Some server error!") } ctx.Logf("response is %#v", resp) for _, c := range resp.Cookies() { ctx.Logf("Cookie send by the server is: %#v\n", c.Name) } ctx.Logf("End-of-eccaProxy handler") //log.Printf("Sleeping for 10 seconds...\n") //time.Sleep(10 * time.Second) return nil, resp // let goproxy send our response }