From cb6a4838cf9cd7bf4dbcebbb1c0cb10a640df4a8 Mon Sep 17 00:00:00 2001 From: dingjun Date: Tue, 16 Mar 2021 15:50:44 +0800 Subject: [PATCH] Initial commit --- config.yaml | 3 ++ go.mod | 8 ++++ go.sum | 8 ++++ handler.go | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ip.txt | 4 ++ main.go | 94 ++++++++++++++++++++++++++++++++++++++++ 6 files changed, 238 insertions(+) create mode 100644 config.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 handler.go create mode 100644 ip.txt create mode 100644 main.go diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..59dece4 --- /dev/null +++ b/config.yaml @@ -0,0 +1,3 @@ + +port: 7777 +ip_list: "ip.txt" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0fad11d --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/fangdingjun/proxy_util + +go 1.15 + +require ( + github.com/fangdingjun/go-log/v5 v5.0.0 + gopkg.in/yaml.v2 v2.4.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..ea0baa7 --- /dev/null +++ b/go.sum @@ -0,0 +1,8 @@ +github.com/fangdingjun/go-log v1.0.20 h1:ZNfkghi6WA1wXg2KkBz8idbqg4M63VezKR63OLX6FEw= +github.com/fangdingjun/go-log/v5 v5.0.0 h1:vdh9Bk9C4ZFL6KoO6rII73zQIyaLf7hFdBvucO/ckiE= +github.com/fangdingjun/go-log/v5 v5.0.0/go.mod h1:V012Oxo0/pSbccX4OFSp9MJglXwNsZo2ByBBorr7zzM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..58ab149 --- /dev/null +++ b/handler.go @@ -0,0 +1,121 @@ +package main + +import ( + "context" + "fmt" + "io" + "math/rand" + "net" + "net/http" + "time" + + log "github.com/fangdingjun/go-log/v5" +) + +func getLocalIP() string { + n := rand.Int() % len(iplist) + return iplist[n] +} + +func proxyHandler(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodConnect { + handleConnect(w, r) + return + } + if r.Method == http.MethodGet || r.Method == http.MethodPost { + handleHTTP(w, r) + return + } + http.Error(w, "unsupported method", http.StatusBadRequest) +} + +func localDialer(ctx context.Context, network, addr string) (net.Conn, error) { + ip := getLocalIP() + laddr := &net.TCPAddr{ + IP: net.ParseIP(ip), + Port: 0, + } + + dialer := &net.Dialer{ + LocalAddr: laddr, + Timeout: 10 * time.Second, + } + return dialer.DialContext(ctx, network, addr) +} + +func handleHTTP(w http.ResponseWriter, r *http.Request) { + r.Header.Del("proxy-connection") + r.Header.Del("proxy-authorization") + tr := &http.Transport{ + DialContext: localDialer, + } + resp, err := tr.RoundTrip(r) + if err != nil { + log.Errorln(err) + http.Error(w, err.Error(), http.StatusServiceUnavailable) + return + } + defer resp.Body.Close() + + hdr := w.Header() + resp.Header.Del("connection") + + for k, v := range resp.Header { + for _, v1 := range v { + hdr.Add(k, v1) + } + } + + w.WriteHeader(resp.StatusCode) + io.Copy(w, resp.Body) +} + +func handleConnect(w http.ResponseWriter, r *http.Request) { + ip := getLocalIP() + + log.Debugf("connect to %s, use local ip %s", r.RequestURI, ip) + + addr := &net.TCPAddr{ + IP: net.ParseIP(ip), + Port: 0, + } + + dialer := &net.Dialer{ + LocalAddr: addr, + Timeout: 10 * time.Second, + } + + conn, err := dialer.Dial("tcp", r.RequestURI) + if err != nil { + log.Errorln(err) + http.Error(w, "connection failed", http.StatusBadGateway) + return + } + + defer conn.Close() + + hj := w.(http.Hijacker) + conn1, _, _ := hj.Hijack() + defer conn1.Close() + + fmt.Fprintf(conn1, "HTTP/1.1 200 connection ok\r\n\r\n") + + ch := make(chan struct{}, 2) + go func() { + io.Copy(conn, conn1) + ch <- struct{}{} + }() + + go func() { + io.Copy(conn1, conn) + ch <- struct{}{} + }() + <-ch +} + +type handler struct { +} + +func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + proxyHandler(w, r) +} diff --git a/ip.txt b/ip.txt new file mode 100644 index 0000000..f216c8f --- /dev/null +++ b/ip.txt @@ -0,0 +1,4 @@ +127.0.0.1 +192.168.56.1 +192.168.0.12 +172.17.0.1 \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..df4561b --- /dev/null +++ b/main.go @@ -0,0 +1,94 @@ +package main + +import ( + "bufio" + "flag" + "fmt" + "io" + "io/ioutil" + "math/rand" + "net/http" + "os" + "strings" + "time" + + log "github.com/fangdingjun/go-log/v5" + "gopkg.in/yaml.v2" +) + +var iplist = []string{} + +// Conf config file +type Conf struct { + Port int `yaml:"port"` + ListFile string `yaml:"ip_list"` +} + +func main() { + var cfg Conf + var cfgfile string + var logfile string + var loglevel string + + flag.StringVar(&cfgfile, "c", "config.yaml", "config file") + flag.StringVar(&logfile, "log_file", "", "log file") + flag.StringVar(&loglevel, "log_level", "INFO", "log level") + flag.Parse() + + lv, err := log.ParseLevel(loglevel) + if err != nil { + fmt.Printf("wrong level name: %s\nvalid level name: DEBUG, INFO, WARN, ERROR, FATAL\n", err) + os.Exit(1) + } + log.Default.Level = lv + + if logfile != "" { + log.Default.Out = &log.FixedSizeFileWriter{ + Name: logfile, + MaxSize: 10 * 1024 * 1024, + MaxCount: 4, + } + } + + rand.Seed(time.Now().Unix()) + + data, err := ioutil.ReadFile(cfgfile) + if err != nil { + log.Fatal(err) + } + if err = yaml.Unmarshal(data, &cfg); err != nil { + log.Fatal(err) + } + loadIPList(cfg.ListFile) + + log.Printf("Listen at http://0.0.0.0:%d", cfg.Port) + + if err = http.ListenAndServe(fmt.Sprintf(":%d", cfg.Port), &handler{}); err != nil { + log.Fatal(err) + } +} + +func loadIPList(f string) { + fp, err := os.Open(f) + if err != nil { + log.Errorln(err) + return + } + defer fp.Close() + + r := bufio.NewReader(fp) + for { + line, err := r.ReadString('\n') + if err != nil { + if err != io.EOF { + log.Errorln(err) + } + break + } + line = strings.Trim(line, " \r\n\t") + if line == "" || line[0] == '#' { + continue + } + iplist = append(iplist, line) + } +}