commit 94db94ce1b9a13382d22b9a8696ba16d5a2fa83c Author: zhengbaoyang Date: Wed Jun 15 14:39:01 2016 +0800 first diff --git a/clcolor.go b/clcolor.go new file mode 100644 index 0000000..5a6672c --- /dev/null +++ b/clcolor.go @@ -0,0 +1,84 @@ +package main + +import ( + "fmt" + "runtime" +) + +const ( + TextBlack = iota + 30 + TextRed + TextGreen + TextYellow + TextBlue + TextMagenta + TextCyan + TextWhite +) + +func Black(str string) string { + return textColor(TextBlack, str) +} + +func Red(str string) string { + return textColor(TextRed, str) +} + +func Green(str string) string { + return textColor(TextGreen, str) +} + +func Yellow(str string) string { + return textColor(TextYellow, str) +} + +func Blue(str string) string { + return textColor(TextBlue, str) +} + +func Magenta(str string) string { + return textColor(TextMagenta, str) +} + +func Cyan(str string) string { + return textColor(TextCyan, str) +} + +func White(str string) string { + return textColor(TextWhite, str) +} + +func textColor(color int, str string) string { + if IsWindows() { + return str + } + + switch color { + case TextBlack: + return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextBlack, str) + case TextRed: + return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextRed, str) + case TextGreen: + return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextGreen, str) + case TextYellow: + return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextYellow, str) + case TextBlue: + return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextBlue, str) + case TextMagenta: + return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextMagenta, str) + case TextCyan: + return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextCyan, str) + case TextWhite: + return fmt.Sprintf("\x1b[0;%dm%s\x1b[0m", TextWhite, str) + default: + return str + } +} + +func IsWindows() bool { + if runtime.GOOS == "windows" { + return true + } else { + return false + } +} diff --git a/gomitmproxy.go b/gomitmproxy.go new file mode 100644 index 0000000..5af60e7 --- /dev/null +++ b/gomitmproxy.go @@ -0,0 +1,330 @@ +/* +Author:shepbao +Time:2016-06-01 +*/ + +package main + +import ( + "bufio" + "bytes" + "compress/flate" + "compress/gzip" + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "io/ioutil" + "math" + "net" + "net/http" + "net/http/httputil" + "net/url" + "os" + "regexp" + "strconv" + "time" + + "log" +) + +const ( + MaxOutstanding = 10 +) + +var Version string = "1.0" +var sem = make(chan int, MaxOutstanding) + +type AccessStatistics struct { + AllCount int64 + HostCount map[string]int64 +} + +var transport = &http.Transport{ + ResponseHeaderTimeout: 30 * time.Second, +} + +//config json +type Config struct { + Port string `json :"port"` //"8080" + Raddr string `json:"raddr,omitempty"` //"localhost:8888" +} + +var port = flag.String("port", "8080", "Listen port") +var raddr = flag.String("raddr", "", "Remote addr") +var monitor = flag.Bool("m", false, "monitor mode") + +var cfg = flag.String("conf", "./config", "config file") +var cf *Config + +var logFile *os.File +var logger *log.Logger + +func main() { + var err error + logFile, err = os.Create("error.log") + if err != nil { + log.Fatalln("fail to create log file!") + } + + logger = log.New(logFile, "[gomitmproxy]", log.LstdFlags|log.Llongfile) + flag.Parse() + + cf, err = ParseConfig(*cfg) + if err != nil { + logger.Println("ParseConfig err:", err) + } else { + if len(cf.Port) == 0 { + log.Fatal("Miss Port") + } + *port = cf.Port + + if len(cf.Raddr) == 0 { + log.Fatal("Miss Raddr") + } + *raddr = cf.Raddr + } + + log.Println("Listen port: ", *port) + if len(*raddr) != 0 { + log.Println("Connect to Raddr: ", *raddr) + } + + ln, err := net.Listen("tcp", ":"+*port) + if err != nil { + logger.Println("listen err:", err) + } + defer ln.Close() + + for { + conn, err := ln.Accept() + if err != nil { + logger.Println("Accept err:", err) + continue + } + go handleConn(conn) + + } +} + +//处理连接 +func handleConn(conn net.Conn) { + reader := bufio.NewReader(conn) + req, err := http.ReadRequest(reader) + if err != nil { + logger.Println("readRequest err:", err) + conn.Close() + return + } + + host := req.Host + matched, _ := regexp.MatchString(":[0-9]+$", host) + if !matched { + host += ":80" + } + conn_proxy, err := Connect(host) + if err != nil { + return + } + defer conn_proxy.Close() + + if req.Method == "CONNECT" { + b := []byte("HTTP/1.1 200 Connection Established\r\n" + + "Proxy-Agent: golang_proxy/" + Version + "\r\n\r\n") + _, err := conn.Write(b) + if err != nil { + logger.Println("Write Connect err:", err) + return + } + } else { + req.Header.Del("Proxy-Connection") + req.Header.Set("Connection", "Keep-Alive") + if err = req.Write(conn_proxy); err != nil { + logger.Println("send to server err", err) + return + } + } + + if *monitor && req.Method != "CONNECT" { + + resp, err := http.ReadResponse(bufio.NewReader(conn_proxy), req) + if err != nil { + logger.Println("read response err:", err) + return + } + + respDump, err := httputil.DumpResponse(resp, true) + if err != nil { + logger.Println("respDump err:", err) + } + + _, err = conn.Write(respDump) + if err != nil { + logger.Println("conn write err:", err) + } + // reqDump, _ := httputil.DumpRequest(req, false) + // log.Println(string(reqDump), string(respDump)) + go httpDump(req, resp) + + } else { + err = Transport(conn, conn_proxy) + if err != nil { + logger.Println("Transport err:", err) + } + } +} + +//连接真实的主机 +func Connect(host string) (net.Conn, error) { + if len(*raddr) == 0 { + conn, err := net.DialTimeout("tcp", host, time.Second*30) + if err != nil { + // logger.Println("connect", host, "err", err) + return nil, err + } + return conn, nil + } + + conn_proxy, err := net.DialTimeout("tcp", *raddr, time.Second*30) + if err != nil { + logger.Println("connect proxy err", err) + return nil, err + } + err = connectProxyServer(conn_proxy, host) + if err != nil { + logger.Println("connectServer err:", err) + return nil, err + } + return conn_proxy, nil +} + +//连接代理服务器 +func connectProxyServer(conn net.Conn, addr string) error { + + req := &http.Request{ + Method: "CONNECT", + URL: &url.URL{Host: addr}, + Host: addr, + ProtoMajor: 1, + ProtoMinor: 1, + Header: make(http.Header), + } + req.Header.Set("Proxy-Connection", "keep-alive") + + if err := req.Write(conn); err != nil { + return err + } + + resp, err := http.ReadResponse(bufio.NewReader(conn), req) + if err != nil { + return err + } + if resp.StatusCode != http.StatusOK { + return errors.New(resp.Status) + } + return nil +} + +//两个io口的连接 +func Transport(conn1, conn2 net.Conn) (err error) { + rChan := make(chan error, 1) + wChan := make(chan error, 1) + + go MyCopy(conn1, conn2, wChan) + go MyCopy(conn2, conn1, rChan) + + select { + case err = <-wChan: + case err = <-rChan: + } + + return +} + +func MyCopy(src io.Reader, dst io.Writer, ch chan<- error) { + _, err := io.Copy(dst, src) + ch <- err +} + +//配置文件解析 +func ParseConfig(cfg string) (*Config, error) { + var conf Config + configContext, err := ioutil.ReadFile(cfg) + if err != nil { + return nil, err + } + err = json.Unmarshal(configContext, &conf) + if err != nil { + return nil, err + } + return &conf, nil +} + +//复制http头 +func copyHeader(src, dst http.Header) { + for k, vv := range src { + for _, v := range vv { + dst.Add(k, v) + } + } +} + +//打印http请求和响应 +func httpDump(req *http.Request, resp *http.Response) { + defer resp.Body.Close() + var respStatusStr string + respStatus := resp.StatusCode + respStatusHeader := int(math.Floor(float64(respStatus / 100))) + switch respStatusHeader { + case 2: + respStatusStr = Green("<--" + strconv.Itoa(respStatus)) + case 3: + respStatusStr = Yellow("<--" + strconv.Itoa(respStatus)) + case 4: + respStatusStr = Magenta("<--" + strconv.Itoa(respStatus)) + case 5: + respStatusStr = Red("<--" + strconv.Itoa(respStatus)) + } + fmt.Println(Green("Request:")) + fmt.Printf("%s %s %s\n", Blue(req.Method), req.RequestURI, respStatusStr) + for headerName, headerContext := range req.Header { + fmt.Printf("%s: %s\n", Blue(headerName), headerContext) + } + fmt.Println(Green("Response:")) + for headerName, headerContext := range resp.Header { + fmt.Printf("%s: %s\n", Blue(headerName), headerContext) + } + + respBody, err := ioutil.ReadAll(resp.Body) + if err != nil { + logger.Println("read resp body err:", err) + } else { + acceptEncode := resp.Header["Content-Encoding"] + var respBodyBin bytes.Buffer + w := bufio.NewWriter(&respBodyBin) + w.Write(respBody) + w.Flush() + for _, compress := range acceptEncode { + switch compress { + case "gzip": + r, err := gzip.NewReader(&respBodyBin) + if err != nil { + logger.Println("gzip reader err:", err) + } else { + defer r.Close() + respBody, _ = ioutil.ReadAll(r) + } + break + case "deflate": + r := flate.NewReader(&respBodyBin) + defer r.Close() + respBody, _ = ioutil.ReadAll(r) + break + } + } + fmt.Printf("%s\n", string(respBody)) + } + + fmt.Printf("%s%s%s\n", Black("####################"), Cyan("END"), Black("####################")) +} diff --git a/proxy.png b/proxy.png new file mode 100644 index 0000000..179d0a8 Binary files /dev/null and b/proxy.png differ