Ejemplo n.º 1
0
func (f *Filter) Request(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Request, error) {
	if auth := req.Header.Get("Proxy-Authorization"); auth != "" {
		req.Header.Del("Proxy-Authorization")
		ctx.SetString(authHeader, auth)
	}
	return ctx, req, nil
}
Ejemplo n.º 2
0
func (f *Filter) RoundTrip(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Response, error) {

	if ip, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
		if _, ok := f.WhiteList[ip]; ok {
			return ctx, nil, nil
		}
	}

	if auth, err := ctx.GetString(authHeader); err == nil {
		if _, ok := f.ByPassHeaders.Get(auth); ok {
			glog.V(3).Infof("auth filter hit bypass cache %#v", auth)
			return ctx, nil, nil
		}
		parts := strings.SplitN(auth, " ", 2)
		if len(parts) == 2 {
			switch parts[0] {
			case "Basic":
				if userpass, err := base64.StdEncoding.DecodeString(parts[1]); err == nil {
					parts := strings.Split(string(userpass), ":")
					user := parts[0]
					pass := parts[1]
					pass1, ok := f.Basic[user]
					if ok && pass == pass1 {
						f.ByPassHeaders.Set(auth, struct{}{}, time.Now().Add(time.Hour))
						return ctx, nil, nil
					}
				}
			default:
				glog.Errorf("Unrecognized auth type: %#v", parts[0])
				break
			}
		}
	}

	glog.V(1).Infof("UnAuthenticated URL %v from %#v", req.URL.String(), req.RemoteAddr)

	noAuthResponse := &http.Response{
		Status:        "407 Proxy authentication required",
		StatusCode:    407,
		Proto:         "HTTP/1.1",
		ProtoMajor:    1,
		ProtoMinor:    1,
		Header:        http.Header{},
		Request:       req,
		Close:         true,
		ContentLength: -1,
	}

	return ctx, noAuthResponse, nil
}
Ejemplo n.º 3
0
func (f *Filter) RoundTrip(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Response, error) {
	if _, ok := f.dialer.hosts.Lookup(req.Host); !ok {
		return ctx, nil, nil
	}

	switch req.Method {
	case "CONNECT":
		glog.Infof("%s \"IPLIST %s %s %s\" - -", req.RemoteAddr, req.Method, req.Host, req.Proto)
		remote, err := f.transport.Dial("tcp", req.Host)
		if err != nil {
			return ctx, nil, err
		}

		switch req.Proto {
		case "HTTP/2.0":
			rw := ctx.GetResponseWriter()
			io.WriteString(rw, "HTTP/1.1 200 OK\r\n\r\n")
			go httpproxy.IoCopy(remote, req.Body)
			httpproxy.IoCopy(rw, remote)
		case "HTTP/1.1", "HTTP/1.0":
			rw := ctx.GetResponseWriter()
			hijacker, ok := rw.(http.Hijacker)
			if !ok {
				return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments Hijacker", rw)
			}
			local, _, err := hijacker.Hijack()
			if err != nil {
				return ctx, nil, err
			}
			local.Write([]byte("HTTP/1.1 200 OK\r\n\r\n"))
			go httpproxy.IoCopy(remote, local)
			httpproxy.IoCopy(local, remote)
		default:
			glog.Warningf("Unkown req=%#v", req)
		}
		ctx.SetHijacked(true)
		return ctx, nil, nil
	case "PRI":
		//TODO: fix for http2
		return ctx, nil, nil
	default:
		resp, err := f.transport.RoundTrip(req)
		if err != nil {
			glog.Errorf("%s \"IPLIST %s %s %s\" error: %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, err)
			data := err.Error()
			resp = &http.Response{
				Status:        "502 Bad Gateway",
				StatusCode:    502,
				Proto:         "HTTP/1.1",
				ProtoMajor:    1,
				ProtoMinor:    1,
				Header:        http.Header{},
				Request:       req,
				Close:         true,
				ContentLength: int64(len(data)),
				Body:          ioutil.NopCloser(bytes.NewReader([]byte(data))),
			}
			err = nil
		} else {
			if req.RemoteAddr != "" {
				glog.Infof("%s \"IPLIST %s %s %s\" %d %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, resp.StatusCode, resp.Header.Get("Content-Length"))
			}
		}
		return ctx, resp, err
	}
}
Ejemplo n.º 4
0
func (f *Filter) Request(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Request, error) {
	if req.Method != "CONNECT" || !f.Match(req.Host) {
		return ctx, req, nil
	}

	hijacker, ok := ctx.GetResponseWriter().(http.Hijacker)
	if !ok {
		return ctx, nil, fmt.Errorf("%#v does not implments Hijacker", ctx.GetResponseWriter())
	}

	conn, _, err := hijacker.Hijack()
	if err != nil {
		return ctx, nil, fmt.Errorf("http.ResponseWriter Hijack failed: %s", err)
	}

	_, err = io.WriteString(conn, "HTTP/1.1 200 OK\r\n\r\n")
	if err != nil {
		conn.Close()
		return ctx, nil, err
	}

	glog.Infof("%s \"STRIP %s %s %s\" - -", req.RemoteAddr, req.Method, req.Host, req.Proto)

	config, err := f.issue(req.Host)
	if err != nil {
		conn.Close()
		return ctx, nil, err
	}

	tlsConn := tls.Server(conn, config)

	if err := tlsConn.Handshake(); err != nil {
		glog.V(2).Infof("%s %T.Handshake() error: %#v", req.RemoteAddr, tlsConn, err)
		conn.Close()
		return ctx, nil, err
	}

	if ln1, ok := ctx.GetListener().(httpproxy.Listener); ok {
		ln1.Add(tlsConn)
		ctx.SetHijacked(true)
		return ctx, nil, nil
	}

	loConn, err := net.Dial("tcp", ctx.GetListener().Addr().String())
	if err != nil {
		return ctx, nil, err
	}

	go httpproxy.IoCopy(loConn, tlsConn)
	go httpproxy.IoCopy(tlsConn, loConn)

	ctx.SetHijacked(true)
	return ctx, nil, nil
}
Ejemplo n.º 5
0
func (f *Filter) RoundTrip(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Response, error) {
	if !f.Sites.Match(req.Host) {
		return ctx, nil, nil
	}

	i := 0
	switch path.Ext(req.URL.Path) {
	case ".jpg", ".png", ".webp", ".bmp", ".gif", ".flv", ".mp4":
		i = rand.Intn(len(f.FetchServers))
	case "":
		name := path.Base(req.URL.Path)
		if strings.Contains(name, "play") ||
			strings.Contains(name, "video") {
			i = rand.Intn(len(f.FetchServers))
		}
	default:
		if strings.Contains(req.URL.Host, "img.") ||
			strings.Contains(req.URL.Host, "cache.") ||
			strings.Contains(req.URL.Host, "video.") ||
			strings.Contains(req.URL.Host, "static.") ||
			strings.HasPrefix(req.URL.Host, "img") ||
			strings.HasPrefix(req.URL.Path, "/static") ||
			strings.HasPrefix(req.URL.Path, "/asset") ||
			strings.Contains(req.URL.Path, "min.js") ||
			strings.Contains(req.URL.Path, "static") ||
			strings.Contains(req.URL.Path, "asset") ||
			strings.Contains(req.URL.Path, "/cache/") {
			i = rand.Intn(len(f.FetchServers))
		}
	}

	fetchServer := f.FetchServers[i]

	if req.Method == "CONNECT" {
		rconn, err := fetchServer.Transport.Connect(req)
		if err != nil {
			return ctx, nil, err
		}
		defer rconn.Close()

		rw := ctx.GetResponseWriter()

		hijacker, ok := rw.(http.Hijacker)
		if !ok {
			return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Hijacker", rw)
		}

		flusher, ok := rw.(http.Flusher)
		if !ok {
			return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Flusher", rw)
		}

		rw.WriteHeader(http.StatusOK)
		flusher.Flush()

		lconn, _, err := hijacker.Hijack()
		if err != nil {
			return ctx, nil, fmt.Errorf("%#v.Hijack() error: %v", hijacker, err)
		}
		defer lconn.Close()

		go httpproxy.IoCopy(rconn, lconn)
		httpproxy.IoCopy(lconn, rconn)

		ctx.SetHijacked(true)
		return ctx, nil, nil
	}
	resp, err := fetchServer.RoundTrip(req)
	if err != nil {
		return ctx, nil, err
	} else {
		glog.Infof("%s \"VPS %s %s %s\" %d %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, resp.StatusCode, resp.Header.Get("Content-Length"))
	}
	return ctx, resp, err
}
Ejemplo n.º 6
0
func (f *Filter) RoundTrip(ctx *filters.Context, req *http.Request) (*filters.Context, *http.Response, error) {
	switch req.Method {
	case "CONNECT":
		glog.Infof("%s \"DIRECT %s %s %s\" - -", req.RemoteAddr, req.Method, req.Host, req.Proto)
		rconn, err := f.transport.Dial("tcp", req.Host)
		if err != nil {
			return ctx, nil, err
		}

		rw := ctx.GetResponseWriter()

		hijacker, ok := rw.(http.Hijacker)
		if !ok {
			return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Hijacker", rw)
		}

		flusher, ok := rw.(http.Flusher)
		if !ok {
			return ctx, nil, fmt.Errorf("http.ResponseWriter(%#v) does not implments http.Flusher", rw)
		}

		rw.WriteHeader(http.StatusOK)
		flusher.Flush()

		lconn, _, err := hijacker.Hijack()
		if err != nil {
			return ctx, nil, fmt.Errorf("%#v.Hijack() error: %v", hijacker, err)
		}
		defer lconn.Close()

		go httpproxy.IoCopy(rconn, lconn)
		httpproxy.IoCopy(lconn, rconn)

		ctx.SetHijacked(true)
		return ctx, nil, nil
	case "PRI":
		//TODO: fix for http2
		return ctx, nil, nil
	default:
		resp, err := f.transport.RoundTrip(req)
		if err == ErrLoopbackAddr {
			http.FileServer(http.Dir(".")).ServeHTTP(ctx.GetResponseWriter(), req)
			ctx.SetHijacked(true)
			return ctx, nil, nil
		}

		if err != nil {
			glog.Errorf("%s \"DIRECT %s %s %s\" error: %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, err)
			data := err.Error()
			resp = &http.Response{
				Status:        "502 Bad Gateway",
				StatusCode:    502,
				Proto:         "HTTP/1.1",
				ProtoMajor:    1,
				ProtoMinor:    1,
				Header:        http.Header{},
				Request:       req,
				Close:         true,
				ContentLength: int64(len(data)),
				Body:          ioutil.NopCloser(bytes.NewReader([]byte(data))),
			}
			err = nil
		} else {
			if req.RemoteAddr != "" {
				glog.Infof("%s \"DIRECT %s %s %s\" %d %s", req.RemoteAddr, req.Method, req.URL.String(), req.Proto, resp.StatusCode, resp.Header.Get("Content-Length"))
			}
		}
		if f.ratelimt.Rate > 0 && resp.ContentLength > f.ratelimt.Threshold {
			glog.V(2).Infof("RateLimit %#v rate to %#v", req.URL.String(), f.ratelimt.Rate)
			resp.Body = httpproxy.NewRateLimitReader(resp.Body, f.ratelimt.Rate, f.ratelimt.Capacity)
		}
		return ctx, resp, err
	}
}