diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..807c23c --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ + +linux: + bash ./scripts/build.sh linux +mac: + bash ./scripts/build.sh mac +windows: + bash ./scripts/build.sh windows +alpine: + bash ./scripts/build.sh alpine +all: + bash ./scripts/build.sh all +clean: + rm -rf dist/* \ No newline at end of file diff --git a/README.md b/README.md index d1a174f..34f3226 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ anyproxy 是一个部署在Linux系统上的tcp流转发器,可以直接将本地或网络收到的请求发出,也可以将请求转到tunneld或SOCKS或charles等代理。可以代替Proxifier做Linux下的客户端, 也可以配合Proxifier当它的服务端。经过跨平台编译,如果只做网络包的转发可以在windows等平台使用。 -[下载Linux包](http://cloudme.io/anyproxy) 、 [下载Mac包](http://cloudme.io/anyproxy-darwin) 、 -[下载Windows包](http://cloudme.io/anyproxy-windows.exe) 、 [下载alpine包](http://cloudme.io/anyproxy-alpine) +[下载Linux包](http://cloudme.io/anyproxy) 、 [下载Mac包](http://cloudme.io/anyproxy-darwin) 、 [下载Windows包](http://cloudme.io/anyproxy-windows.exe) 提醒:请使用浏览器右键的“链接另存为”下载文件 @@ -42,8 +41,6 @@ tunneld 是一个anyproxy的服务端,部署在服务器上接收anyproxy的 `使用iptables将本用户下tcp流转到anyproxy,再进行docker pull操作` -![解决Docker pull问题](examples/docker_pull.png) - > 案例2: 解决相同域名访问网站不同测试环境的问题 `本地通过内网 anyproxy 代理上网,遇到测试服务器域名则跳到外网tunneld转发,网站的nginx根据来源IP进行转发到特定测试环境(有几个环境就需要有几个tunneld服务且IP要不同)` @@ -68,7 +65,7 @@ go env -w GOPROXY=https://goproxy.cn,direct ``` git clone https://github.com/keminar/anyproxy.git cd anyproxy -go build anyproxy.git +make all ``` > 本机启动 @@ -81,10 +78,10 @@ sudo -u anyproxy ./anyproxy ./anyproxy -daemon # 示例3. 启动tunneld -./tunneld +./anyproxy -mode tunnel # 示例4. 启动anyproxy并将请求转给tunneld -./anyproxy -p '127.0.0.1:3001' +./anyproxy -p 'tunnel://127.0.0.1:3001' # 示例5. 启动anyproxy并将请求转给socks5 ./anyproxy -p 'socks5://127.0.0.1:10000' @@ -170,12 +167,11 @@ sudo iptables -t nat -D OUTPUT 2 * ~~支持windows平台使用~~ * ~~通过websocket实现内网穿透(必须为http的非CONNECT请求)~~ * ~~订阅增加邮箱标识,用于辨别在线用户~~ -* TCP 增加更多协议解析支持,如rtmp,ftp, socks5, https(SNI)等 -* TCP 转发的mysql的连接请求会一直卡住 -* 与Tunnel的多账户认证,账户可设置有效期 -* HTTP/1.1 keep-alive后端也能复用tcp +* ~~与Tunnel功能合并,使用mode区分~~ * ~~启用ws-listen后的平滑重启问题~~ * ~~监听配置文件变化重新加载路由~~ +* TCP 增加更多协议解析支持,如rtmp,ftp, socks5, https(SNI)等 +* TCP 转发的mysql的连接请求会一直卡住 # 感谢 diff --git a/anyproxy.go b/anyproxy.go index ae29447..c1f2151 100644 --- a/anyproxy.go +++ b/anyproxy.go @@ -8,6 +8,7 @@ import ( "net/http" _ "net/http/pprof" "os" + "time" "github.com/keminar/anyproxy/config" "github.com/keminar/anyproxy/grace" @@ -25,6 +26,7 @@ var ( gProxyServerSpec string gWebsocketListen string gWebsocketConn string + gMode string gHelp bool gDebug int gPprof string @@ -36,10 +38,10 @@ func init() { flag.StringVar(&gProxyServerSpec, "p", "", "Proxy servers to use") flag.StringVar(&gWebsocketListen, "ws-listen", "", "Websocket address and port to listen on") flag.StringVar(&gWebsocketConn, "ws-connect", "", "Websocket Address and port to connect") + flag.StringVar(&gMode, "mode", "", "Run mode(proxy, tunnel). proxy mode default") flag.IntVar(&gDebug, "debug", 0, "debug mode (0, 1, 2)") flag.StringVar(&gPprof, "pprof", "", "pprof port, disable if empty") flag.BoolVar(&gHelp, "h", false, "This usage message") - } func main() { @@ -57,10 +59,7 @@ func main() { } cmdName := "anyproxy" - logDir := "./logs/" - if conf.RouterConfig.Log.Dir != "" { - logDir = conf.RouterConfig.Log.Dir - } + logDir := config.IfEmptyThen(conf.RouterConfig.Log.Dir, "./logs/", "") envRunMode := fmt.Sprintf("%s_run_mode", cmdName) fd := logging.ErrlogFd(logDir, cmdName) // 是否后台运行 @@ -89,23 +88,33 @@ func main() { //浏览器访问: http://:5001/debug/pprof/ log.Println("Starting pprof debug server ...") // 这里不要使用log.Fatal会在平滑重启时导致进程退出 - // 因为http server现在没办法加入平滑重启,第一次重启会报端口冲突,可以通过重启两次来启动到pprof - log.Println(http.ListenAndServe(gPprof, nil)) + // 因为http server现在没办法一次平滑重启,会报端口冲突,可以通过多次重试来启动pprof + for i := 0; i < 10; i++ { + log.Println(http.ListenAndServe(gPprof, nil)) + time.Sleep(10 * time.Second) + } }() } - // websocket 配置 + // websocket 服务端 gWebsocketListen = config.IfEmptyThen(gWebsocketListen, conf.RouterConfig.Websocket.Listen, "") if gWebsocketListen != "" { gWebsocketListen = tools.FillPort(gWebsocketListen) go nat.NewServer(&gWebsocketListen) } - + // websocket 客户端 gWebsocketConn = config.IfEmptyThen(gWebsocketConn, conf.RouterConfig.Websocket.Connect, "") if gWebsocketConn != "" { gWebsocketConn = tools.FillPort(gWebsocketConn) go nat.ConnectServer(&gWebsocketConn) } - server := grace.NewServer(gListenAddrPort, proto.ClientHandler) - server.ListenAndServe() + + // 运行模式 + if gMode == "tunnel" { + server := grace.NewServer(gListenAddrPort, proto.ServerHandler) + server.ListenAndServe() + } else { + server := grace.NewServer(gListenAddrPort, proto.ClientHandler) + server.ListenAndServe() + } } diff --git a/conf/router.yaml b/conf/router.yaml index 5e242ef..e5018d6 100644 --- a/conf/router.yaml +++ b/conf/router.yaml @@ -6,7 +6,7 @@ log: # 监听配置文件变化 watcher: true # anyproxy 和 tunnel通信密钥, 必须16位长度 -#token: anyproxyproxyany +token: anyproxyproxyany # 可访问的客户端IP,为空不限制 #allowIP: diff --git a/nat/conn.go b/nat/conn.go index 24f8ff2..001e7de 100644 --- a/nat/conn.go +++ b/nat/conn.go @@ -68,18 +68,13 @@ func NewServer(addr *string) { log.Println(fmt.Sprintf("Listening for websocket connections on %s", *addr)) - i := 0 - for { + for i := 0; i < 1000; i++ { // 副服务,出错不退出并定时重试。方便主服务做平滑重启 err := http.ListenAndServe(*addr, nil) if err != nil { - log.Println("ListenAndServe: ", err) - } - if i > 1000 { - break + log.Println(fmt.Sprintf("ListenAndServe: num=%d, err=%v ", i, err)) } time.Sleep(10 * time.Second) - i++ } } diff --git a/scripts/build.sh b/scripts/build.sh index 0755e3f..8370eb8 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -5,38 +5,44 @@ cd $ROOT_DIR mkdir -p dist/ +# 路径 +GOCMD="go" +GITCMD="git" + +# 目标文件前缀 +BIN="anyproxy" + # 版本号 -VER=`git describe --tags $(git rev-list --tags --max-count=1)` -GOVER=`go version` -COMMIT_SHA1=`git rev-parse HEAD` +ARCH="amd64" + +#组装变量 +GOBUILD="${GOCMD} build" +VER=`${GITCMD} describe --tags $(${GITCMD} rev-list --tags --max-count=1)` +GOVER=`${GOCMD} version` +COMMIT_SHA1=`${GITCMD} rev-parse HEAD` HELP_PRE="github.com/keminar/anyproxy/utils/help" LDFLAGS="-X '${HELP_PRE}.goVersion=${GOVER}'" LDFLAGS="${LDFLAGS} -X '${HELP_PRE}.gitHash=${COMMIT_SHA1}'" LDFLAGS="${LDFLAGS} -X '${HELP_PRE}.version=${VER}'" -# anyproxy -echo "build anyproxy" -# for linux -echo " for linux" -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$LDFLAGS" -o dist/anyproxy-${VER} anyproxy.go - -# for mac -echo " for mac" -CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$LDFLAGS" -o dist/anyproxy-darwin-${VER} anyproxy.go - -# for windows -echo " for windows" -CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$LDFLAGS" -o dist/anyproxy-windows-${VER}.exe anyproxy.go - -# for alpine -echo " for alpine" -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags netgo -ldflags "$LDFLAGS" -o dist/anyproxy-alpine-${VER} anyproxy.go - -# tunneld -echo "build tunneld" -echo " for linux" -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$LDFLAGS" -o dist/tunneld-${VER} tunnel/tunneld.go - -# for alpine -echo " for alpine" -CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags netgo -ldflags "$LDFLAGS" -o dist/tunneld-alpine-${VER} tunnel/tunneld.go \ No newline at end of file +# 编译 +echo "build ..." +if [ "$1" == "all" ] || [ "$1" == "linux" ] ;then + echo " for linux" + CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH} ${GOBUILD} -ldflags "$LDFLAGS" -o dist/${BIN}-${ARCH}-${VER} anyproxy.go +fi + +if [ "$1" == "all" ] || [ "$1" == "mac" ] ;then + echo " for mac" + CGO_ENABLED=0 GOOS=darwin GOARCH=${ARCH} ${GOBUILD} -ldflags "$LDFLAGS" -o dist/${BIN}-darwin-${ARCH}-${VER} anyproxy.go +fi + +if [ "$1" == "all" ] || [ "$1" == "windows" ] ;then + echo " for windows" + CGO_ENABLED=0 GOOS=windows GOARCH=${ARCH} ${GOBUILD} -ldflags "$LDFLAGS" -o dist/${BIN}-windows-${ARCH}-${VER}.exe anyproxy.go +fi + +if [ "$1" == "all" ] || [ "$1" == "alpine" ] ;then + echo " for alpine" + CGO_ENABLED=0 GOOS=linux GOARCH=${ARCH} ${GOBUILD} -tags netgo -ldflags "$LDFLAGS" -o dist/${BIN}-alpine-${ARCH}-${VER} anyproxy.go +fi \ No newline at end of file diff --git a/tunnel/tunneld.go b/tunnel/tunneld.go deleted file mode 100644 index 7fb91fe..0000000 --- a/tunnel/tunneld.go +++ /dev/null @@ -1,77 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "io" - "os" - - "github.com/keminar/anyproxy/config" - "github.com/keminar/anyproxy/grace" - "github.com/keminar/anyproxy/logging" - "github.com/keminar/anyproxy/proto" - "github.com/keminar/anyproxy/utils/conf" - "github.com/keminar/anyproxy/utils/daemon" - "github.com/keminar/anyproxy/utils/help" - "github.com/keminar/anyproxy/utils/tools" -) - -var ( - gListenAddrPort string - gProxyServerSpec string - gHelp bool - gDebug int -) - -func init() { - flag.Usage = help.TunnelUsage - flag.StringVar(&gListenAddrPort, "l", "", "Address and port to listen on") - flag.StringVar(&gProxyServerSpec, "p", "", "Proxy servers to use") - //CONNECT请求不支持ws-listen 和 ws-connect,且tunnel只做接收anyproxy的安全转发,不需要支持ws - flag.IntVar(&gDebug, "debug", 0, "debug mode (0, 1, 2)") - flag.BoolVar(&gHelp, "h", false, "This usage message") -} - -func main() { - flag.Parse() - if gHelp { - flag.Usage() - return - } - - config.SetDebugLevel(gDebug) - conf.LoadAllConfig() - // 检查配置是否存在 - if conf.RouterConfig == nil { - os.Exit(2) - } - - cmdName := "tunneld" - logDir := "./logs/" - if conf.RouterConfig.Log.Dir != "" { - logDir = conf.RouterConfig.Log.Dir - } - envRunMode := fmt.Sprintf("%s_run_mode", cmdName) - fd := logging.ErrlogFd(logDir, cmdName) - // 是否后台运行 - daemon.Daemonize(envRunMode, fd) - - gListenAddrPort = config.IfEmptyThen(gListenAddrPort, conf.RouterConfig.Listen, ":3001") - gListenAddrPort = tools.FillPort(gListenAddrPort) - - var writer io.Writer - // 前台执行,daemon运行根据环境变量识别 - if daemon.IsForeground(envRunMode) { - // 同时输出到日志和标准输出 - writer = io.Writer(os.Stdout) - } - - logging.SetDefaultLogger(logDir, cmdName, true, 3, writer) - // 设置代理 - gProxyServerSpec = config.IfEmptyThen(gProxyServerSpec, conf.RouterConfig.Default.Proxy, "") - config.SetProxyServer(gProxyServerSpec) - - // 与anyproxy不同之处在ServerHandler - server := grace.NewServer(gListenAddrPort, proto.ServerHandler) - server.ListenAndServe() -} diff --git a/utils/help/help.go b/utils/help/help.go index fab2ca0..2bde782 100644 --- a/utils/help/help.go +++ b/utils/help/help.go @@ -28,6 +28,7 @@ func Usage() { fmt.Fprintf(os.Stdout, " -ws-listen Websocket address and port to listen on\n") fmt.Fprintf(os.Stdout, " -ws-connect Websocket Address and port to connect\n") fmt.Fprintf(os.Stdout, " -daemon Run as a Unix daemon\n") + fmt.Fprintf(os.Stdout, " -mode Run mode(proxy, tunnel). proxy mode default\n") fmt.Fprintf(os.Stdout, " -debug Debug mode (0, 1, 2, 3)\n") fmt.Fprintf(os.Stdout, " -pprof Pprof port, disable if empty\n") fmt.Fprintf(os.Stdout, " -h This usage message\n\n") @@ -57,25 +58,6 @@ func Usage() { fmt.Fprintf(os.Stdout, "Thanks to https://github.com/ryanchapman/go-any-proxy.git\n") } -// TunnelUsage 帮助 -func TunnelUsage() { - fmt.Fprintf(os.Stdout, "%s\n\n", versionString("tunneld")) - fmt.Fprintf(os.Stdout, "Usage: %s -l listenaddress -p proxies \n", os.Args[0]) - fmt.Fprintf(os.Stdout, " Proxies anyproxy reqest\n\n") - fmt.Fprintf(os.Stdout, "Mandatory\n") - fmt.Fprintf(os.Stdout, " -l=ADDRPORT Address and port to listen on (e.g., :3001 or 127.0.0.1:3001)\n") - fmt.Fprintf(os.Stdout, "Optional\n") - fmt.Fprintf(os.Stdout, " -p=PROXIES Address and ports of upstream proxy servers to use\n") - fmt.Fprintf(os.Stdout, " (e.g., 10.1.1.1:80 will use http proxy, socks5://10.2.2.2:3128 use socks5 proxy,\n") - fmt.Fprintf(os.Stdout, " tunnel://10.2.2.2:3001 use tunnel proxy)\n") - fmt.Fprintf(os.Stdout, " -daemon Run as a Unix daemon\n") - fmt.Fprintf(os.Stdout, " -debug Debug mode (0, 1, 2, 3)\n") - fmt.Fprintf(os.Stdout, " -h This usage message\n\n") - - fmt.Fprintf(os.Stdout, "Report bugs to https://github.com/keminar/anyproxy or .\n") - fmt.Fprintf(os.Stdout, "Thanks to https://github.com/ryanchapman/go-any-proxy.git\n") -} - func versionString(name string) (v string) { now := time.Now().Unix() buildNum := strings.ToUpper(strconv.FormatInt(now, 36))