├── .gitignore ├── README-zh.md ├── README.md ├── config ├── autostart ├── autostart.exe ├── config.ini ├── proxysetting.exe └── wininet.dll ├── data ├── proxy.json └── services.json ├── docs └── image │ ├── add.png │ ├── delete.png │ ├── empty.jpg │ ├── log.png │ ├── login.png │ ├── preview.png │ ├── setting.png │ ├── start.jpg │ └── update.png ├── go.mod ├── go.sum ├── lib ├── goproxy │ ├── sdk │ │ └── android-ios │ │ │ ├── .gitignore │ │ │ ├── dns.go │ │ │ ├── release_android.sh │ │ │ ├── release_ios.sh │ │ │ └── sdk.go │ ├── services │ │ ├── http │ │ │ └── http.go │ │ ├── kcpcfg │ │ │ └── args.go │ │ ├── mux │ │ │ ├── mux_bridge.go │ │ │ ├── mux_client.go │ │ │ └── mux_server.go │ │ ├── service.go │ │ ├── socks │ │ │ ├── socks.go │ │ │ └── udp.go │ │ ├── sps │ │ │ ├── socksudp.go │ │ │ └── sps.go │ │ ├── tcp │ │ │ └── tcp.go │ │ ├── tunnel │ │ │ ├── tunnel_bridge.go │ │ │ ├── tunnel_client.go │ │ │ └── tunnel_server.go │ │ └── udp │ │ │ └── udp.go │ └── utils │ │ ├── aes │ │ └── aes.go │ │ ├── conncrypt │ │ └── conncrypt.go │ │ ├── functions.go │ │ ├── id │ │ └── xid.go │ │ ├── io-limiter.go │ │ ├── leakybuf.go │ │ ├── map.go │ │ ├── serve-channel.go │ │ ├── sni │ │ └── sni.go │ │ ├── socks │ │ ├── client.go │ │ ├── server.go │ │ └── structs.go │ │ └── structs.go └── webtail │ └── webtail.go ├── main.go ├── release.sh ├── resource.syso ├── school.ico ├── server ├── log.go ├── login.go ├── proxy.go └── server.go ├── static ├── .DS_Store ├── css │ ├── bootstrap-theme.min.css │ ├── bootstrap-theme.min.css.map │ ├── bootstrap.min.css │ ├── bootstrap.min.css.map │ └── price.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── images │ └── NX-Desktop-BG.png ├── js │ ├── bootstrap.min.js │ ├── jquery-1.12.4.min.js │ ├── layer.js │ ├── notify.js │ ├── npm.js │ └── theme │ │ └── default │ │ ├── icon-ext.png │ │ ├── icon.png │ │ ├── layer.css │ │ ├── loading-0.gif │ │ ├── loading-1.gif │ │ └── loading-2.gif └── webupload │ ├── Uploader.swf │ ├── demo.js │ ├── webuploader.css │ ├── webuploader.js │ └── webuploader.min.js ├── utils ├── always.go ├── compress.go ├── config.go ├── convert.go ├── data.go └── error.go ├── versioninfo.json └── view ├── index.html └── login.html /.gitignore: -------------------------------------------------------------------------------- 1 | proxy-web 2 | proxy-web.exe 3 | *.exe~ 4 | /db/bucket/ 5 | /upload/ 6 | /data/services/ 7 | /tmp/ 8 | .idea/ 9 | /log/ 10 | .DS_Store 11 | -------------------------------------------------------------------------------- /README-zh.md: -------------------------------------------------------------------------------- 1 | # proxy-web详细介绍 2 | proxy-web是用go语言写的,基于[snail007/goproxy](https://github.com/snail007/goproxy/)完成的可视化网页应用 3 | 4 | --- 5 | [![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy/) 6 | 7 | ### 使用前须知 8 | - [作用](#作用) 9 | - [下载](#下载) 10 | - [更新](#更新) 11 | - [配置](#配置) 12 | - [依赖包](#依赖包) 13 | 14 | ### 手册目录 15 | - [1. 使用](#1使用) 16 | - [2. 参数介绍](#2参数介绍) 17 | 18 | ### 作用 19 | 1、 用web界面的方式使用goproxy,更加方便 20 | 2、 监控goproxy运行情况 21 | 3、 实时显示goproxy产生的日志 22 | 4、 启动proxy-web后能自启动goproxy 23 | 5、 能自开机自启动proxy-web 24 | 6、 可以设置linux,mac,windows全局http代理,需要root权限 25 | 7、 页面全新升级 26 | 27 | ### 下载 28 | [下载地址](https://github.com/yincongcyincong/proxy-web/releases) 29 | 30 | ### 更新 31 | v 2.0 全面更新 32 | 可以自由配置参数 33 | 开机自启动proxy-web 34 | 全局http代理设置 35 | 使用goproxy提供的sdk,不再依赖goproxy二进制程序   36 | 37 | ### 配置 38 | 配置文件为config/config.ini   39 | 可以配置的属性有:端口(默认48080),登录账号和密码(都为admin)   40 | 41 | 42 | ### 依赖包 43 | [github.com/snail007/goproxy/sdk](https://github.com/snail007/goproxy/blob/master/sdk/README.md)goproxy的sdk 44 | [github.com/Unknwon/goconfig](https://github.com/Unknwon/goconfig)解析配置文件 45 | [github.com/astaxie/beego/tree/master/session](https://github.com/astaxie/beego/tree/master/session) session模块 46 | 这些依赖已经在源码内解决,无需go get 47 | 48 | ### 1.使用 49 | 使用48080端口进入页面(如:localhost:48080),首先到登录页面 50 | 51 | 账号密码都为admin,登录进入 52 | 53 | 点击,添加代理,显示添加代理的弹框,可以选择代理是否开启proxy-web服务时也自动启动 54 | 55 | 修改操作 56 | 57 | 启动操作 58 | 59 | 查看日志操作 60 | 61 | 删除操作 62 | 63 | 设置全局http代理和是否开机自启动,这两个操作需要root权限 64 | 65 | 66 | ### 2.参数介绍 67 | 名称:代理的名称。 68 | 参数:指[snail007/goproxy](https://github.com/snail007/goproxy/)中的各种参数。 69 | 70 | ### 源码使用   71 | - 使用非windows编译,请删除resource.syso   72 | - git下载源码   73 |   74 | ### TODO 75 | - -查找bug 76 | 77 | ### License 78 | - under GPLv3 license 79 | 80 | ### Contact 81 | - QQ群:189618940 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # proxy-web introduction 2 | proxy-web is a webview proxy application written by Golang,base on [snail007/goproxy](https://github.com/snail007/goproxy/) 3 | 中文介绍:[proxy-web](https://github.com/yincongcyincong/proxy-web/blob/master/README-zh.md) 4 | --- 5 | [![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy/) 6 | 7 | ### Notice before use 8 | - [Function](#Function) 9 | - [Download](#Download) 10 | - [Update](#Update) 11 | - [Conf](#Conf) 12 | - [Dependence](#Dependence) 13 | 14 | ### Handbook Catalog 15 | - [1. usage](#1usage) 16 | - [2. param introduction](#2param introduction) 17 | 18 | ### Function 19 | 1. Using goproxy through a web interface is more convenient 20 | 2. Monitor the running status of goproxy 21 | 3. Real time display of logs generated by goproxy 22 | 4. After starting proxy web, goproxy can be started automatically 23 | 5. Capable of booting up and booting proxy web on its own 24 | 6. Linux, Mac, and Windows global HTTP proxies can be set, requiring root privileges 25 | 7. New page upgrade 26 | 27 | ### Download 28 | [download websit](https://github.com/yincongcyincong/proxy-web/releases) 29 | 30 | ### Update 31 | v 2.0 overall update 32 | Can freely configure parameters 33 | Power on and self start proxy web 34 | Global HTTP proxy settings 35 | Use the SDK provided by goproxy and no longer rely on goproxy binary programs 36 | 37 | ### Configure 38 | The configuration file is config/config.ini 39 | The configurable properties include: port (default 48080), login account, and password (all admin) 40 | 41 | 42 | ### Dependence 43 | [github.com/snail007/goproxy/sdk](https://github.com/snail007/goproxy/blob/master/sdk/README.md) 44 | [github.com/Unknwon/goconfig](https://github.com/Unknwon/goconfig) 45 | [github.com/astaxie/beego/tree/master/session](https://github.com/astaxie/beego/tree/master/session) 46 | 47 | ### 1.Usage 48 | Use port 48080 to enter the page (e.g. localhost:48080), first go to the login page 49 | 50 | both username and password are admin,then login 51 | 52 | Click to add the proxy and display a pop-up box for adding the proxy. You can choose whether the proxy will automatically start when the proxy web service is enabled 53 | 54 | update 55 | 56 | start 57 | 58 | check out log 59 | 60 | delete 61 | 62 | Setting the global HTTP proxy and whether to boot automatically requires root privileges 63 | 64 | 65 | ### 2.param introduction 66 | name:proxy name。 67 | param:[snail007/goproxy](https://github.com/snail007/goproxy/) have more param detail, please refer。 68 | 69 | ### Usage of source code 70 | - Using non Windows compilation, please remove resource.syso 71 | - git download source code 72 | 73 | ### TODO 74 | - -check out bug 75 | 76 | ### License 77 | - under GPLv3 license 78 | 79 | 80 | -------------------------------------------------------------------------------- /config/autostart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/config/autostart -------------------------------------------------------------------------------- /config/autostart.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/config/autostart.exe -------------------------------------------------------------------------------- /config/config.ini: -------------------------------------------------------------------------------- 1 | [proxy_server] 2 | port = :48080 3 | username = admin 4 | password = admin 5 | services = /data/services.json 6 | 7 | [config] 8 | auto_start = true 9 | proxy = false 10 | 11 | -------------------------------------------------------------------------------- /config/proxysetting.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/config/proxysetting.exe -------------------------------------------------------------------------------- /config/wininet.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/config/wininet.dll -------------------------------------------------------------------------------- /data/proxy.json: -------------------------------------------------------------------------------- 1 | {"ip":"127.0.0.1","port":"18080"} -------------------------------------------------------------------------------- /data/services.json: -------------------------------------------------------------------------------- 1 | {"1533439640321":"yes"} -------------------------------------------------------------------------------- /docs/image/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/docs/image/add.png -------------------------------------------------------------------------------- /docs/image/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/docs/image/delete.png -------------------------------------------------------------------------------- /docs/image/empty.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/docs/image/empty.jpg -------------------------------------------------------------------------------- /docs/image/log.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/docs/image/log.png -------------------------------------------------------------------------------- /docs/image/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/docs/image/login.png -------------------------------------------------------------------------------- /docs/image/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/docs/image/preview.png -------------------------------------------------------------------------------- /docs/image/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/docs/image/setting.png -------------------------------------------------------------------------------- /docs/image/start.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/docs/image/start.jpg -------------------------------------------------------------------------------- /docs/image/update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/docs/image/update.png -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/yincongcyincong/proxy-web 2 | 3 | go 1.18 4 | 5 | require ( 6 | github.com/Unknwon/goconfig v0.0.0-20180308125533-ef1e4c783f8f 7 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc 8 | github.com/astaxie/beego v1.9.3-0.20171218111859-f16688817aa4 9 | github.com/gobwas/ws v0.1.1-0.20180527201045-2c0cb1ddcc09 10 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db 11 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb 12 | github.com/julienschmidt/httprouter v1.1.1-0.20180411154501-adbc77eec0d9 13 | github.com/miekg/dns v0.0.0-20180701183735-3e6e47bc11bc 14 | github.com/pkg/errors v0.8.1-0.20180311214515-816c9085562c 15 | github.com/pmylund/go-cache v2.1.1-0.20180527043350-9f6ff22cfff8+incompatible 16 | github.com/xtaci/kcp-go v2.0.4-0.20180203133237-42bc1dfefff5+incompatible 17 | golang.org/x/crypto v0.21.0 18 | golang.org/x/net v0.22.0 19 | golang.org/x/time v0.5.0 20 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 21 | ) 22 | 23 | require ( 24 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect 25 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect 26 | github.com/gobwas/pool v0.2.0 // indirect 27 | github.com/klauspost/cpuid v1.2.0 // indirect 28 | github.com/klauspost/reedsolomon v0.0.0-20180630081529-3133c51b912e // indirect 29 | github.com/smartystreets/goconvey v1.8.1 // indirect 30 | github.com/stretchr/testify v1.9.0 // indirect 31 | github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 // indirect 32 | github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 // indirect 33 | github.com/tjfoc/gmsm v1.0.2-0.20180622091801-881917d33b84 // indirect 34 | golang.org/x/sys v0.18.0 // indirect 35 | ) 36 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Unknwon/goconfig v0.0.0-20180308125533-ef1e4c783f8f h1:2h0zBHX3qNDltgnb2FiMJWYTcqJVDdUeHQU2/5CTc5w= 2 | github.com/Unknwon/goconfig v0.0.0-20180308125533-ef1e4c783f8f/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw= 3 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= 4 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= 6 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 7 | github.com/astaxie/beego v1.9.3-0.20171218111859-f16688817aa4 h1:dNIynF6ICiq1NghlpIBxljb2JbyC61/JqWB5A9cfUfo= 8 | github.com/astaxie/beego v1.9.3-0.20171218111859-f16688817aa4/go.mod h1:0R4++1tUqERR0WYFWdfkcrsyoVBCG4DgpDGokT3yb+U= 9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 10 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= 11 | github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= 12 | github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= 13 | github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= 14 | github.com/gobwas/ws v0.1.1-0.20180527201045-2c0cb1ddcc09 h1:GMq0fofcJ2Y57HIQC7gYFXyV5/scIzQSGpla0dfvfG4= 15 | github.com/gobwas/ws v0.1.1-0.20180527201045-2c0cb1ddcc09/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= 16 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= 17 | github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 18 | github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g= 19 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= 20 | github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= 21 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 22 | github.com/julienschmidt/httprouter v1.1.1-0.20180411154501-adbc77eec0d9 h1:fYQgw8kxM2StYZLTlDcpfXWm0trPjcJzDtYzwIr02SM= 23 | github.com/julienschmidt/httprouter v1.1.1-0.20180411154501-adbc77eec0d9/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 24 | github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= 25 | github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= 26 | github.com/klauspost/reedsolomon v0.0.0-20180630081529-3133c51b912e h1:PU55RoO5I45ARU29WnEiub4UbSw/dwt2RPuA3JgxNEs= 27 | github.com/klauspost/reedsolomon v0.0.0-20180630081529-3133c51b912e/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= 28 | github.com/miekg/dns v0.0.0-20180701183735-3e6e47bc11bc h1:/hpKsb38tsHA7imIvePK+FrunsKXZI5OyA5N4E3jYPc= 29 | github.com/miekg/dns v0.0.0-20180701183735-3e6e47bc11bc/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 30 | github.com/pkg/errors v0.8.1-0.20180311214515-816c9085562c h1:SZvPVPsWE261bl8uxQ6Siq+ExNmYomz4CTU9E0ALgj4= 31 | github.com/pkg/errors v0.8.1-0.20180311214515-816c9085562c/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 32 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 33 | github.com/pmylund/go-cache v2.1.1-0.20180527043350-9f6ff22cfff8+incompatible h1:yDaaUQeNjNPFE5yPKv8x9fkqLJpU2gveyGV1o5oUo6A= 34 | github.com/pmylund/go-cache v2.1.1-0.20180527043350-9f6ff22cfff8+incompatible/go.mod h1:hmz95dGvINpbRZGsqPcd7B5xXY5+EKb5PpGhQY3NTHk= 35 | github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY= 36 | github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY= 37 | github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60= 38 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 39 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 40 | github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 h1:K+jtWCOuZgCra7eXZ/VWn2FbJmrA/D058mTXhh2rq+8= 41 | github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= 42 | github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 h1:pexgSe+JCFuxG+uoMZLO+ce8KHtdHGhst4cs6rw3gmk= 43 | github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= 44 | github.com/tjfoc/gmsm v1.0.2-0.20180622091801-881917d33b84 h1:vRHy5P+8dmQKnXoQ+hIdD6tZGW00LQEn0/sPcBbbRzU= 45 | github.com/tjfoc/gmsm v1.0.2-0.20180622091801-881917d33b84/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc= 46 | github.com/xtaci/kcp-go v2.0.4-0.20180203133237-42bc1dfefff5+incompatible h1:hygiAxAfy0/0B2lqN7XDHWFarQgyd2agMExOzFXgnbw= 47 | github.com/xtaci/kcp-go v2.0.4-0.20180203133237-42bc1dfefff5+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE= 48 | golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= 49 | golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= 50 | golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= 51 | golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 52 | golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= 53 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 54 | golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= 55 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 56 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 57 | gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= 58 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 59 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 60 | -------------------------------------------------------------------------------- /lib/goproxy/sdk/android-ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.jar 2 | *.aar 3 | *.tar.gz 4 | ios 5 | android 6 | Proxy.framework 7 | -------------------------------------------------------------------------------- /lib/goproxy/sdk/android-ios/dns.go: -------------------------------------------------------------------------------- 1 | package proxy 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "fmt" 7 | services2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services" 8 | kcpcfg2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services/kcpcfg" 9 | logger "log" 10 | "net" 11 | "runtime/debug" 12 | "time" 13 | 14 | "golang.org/x/net/proxy" 15 | 16 | "github.com/miekg/dns" 17 | gocache "github.com/pmylund/go-cache" 18 | ) 19 | 20 | type DNSArgs struct { 21 | ParentServiceType *string 22 | ParentType *string 23 | Parent *string 24 | ParentAuth *string 25 | ParentKey *string 26 | ParentCompress *bool 27 | KCP kcpcfg2.KCPConfigArgs 28 | CertFile *string 29 | KeyFile *string 30 | CaCertFile *string 31 | Local *string 32 | Timeout *int 33 | RemoteDNSAddress *string 34 | DNSTTL *int 35 | CacheFile *string 36 | LocalSocks5Port *string 37 | } 38 | type DNS struct { 39 | cfg DNSArgs 40 | log *logger.Logger 41 | cache *gocache.Cache 42 | exitSig chan bool 43 | serviceKey string 44 | dialer proxy.Dialer 45 | } 46 | 47 | func NewDNS() services2.Service { 48 | return &DNS{ 49 | cfg: DNSArgs{}, 50 | exitSig: make(chan bool, 1), 51 | serviceKey: "dns-service-" + fmt.Sprintf("%d", time.Now().UnixNano()), 52 | } 53 | } 54 | func (s *DNS) CheckArgs() (err error) { 55 | return 56 | } 57 | func (s *DNS) InitService() (err error) { 58 | s.cache = gocache.New(time.Second*time.Duration(*s.cfg.DNSTTL), time.Second*60) 59 | s.cache.LoadFile(*s.cfg.CacheFile) 60 | go func() { 61 | for { 62 | select { 63 | case <-s.exitSig: 64 | return 65 | case <-time.After(time.Second * 60): 66 | err := s.cache.SaveFile(*s.cfg.CacheFile) 67 | if err == nil { 68 | //s.log.Printf("cache saved: %s", *s.cfg.CacheFile) 69 | } else { 70 | s.log.Printf("cache save failed: %s, %s", *s.cfg.CacheFile, err) 71 | } 72 | } 73 | } 74 | }() 75 | s.dialer, err = proxy.SOCKS5("tcp", *s.cfg.Parent, 76 | nil, 77 | &net.Dialer{ 78 | Timeout: 5 * time.Second, 79 | KeepAlive: 2 * time.Second, 80 | }, 81 | ) 82 | if err != nil { 83 | return 84 | } 85 | 86 | sdkArgs := fmt.Sprintf("sps -S %s -T %s -P %s -C %s -K %s -i %d -p 127.0.0.1:%s --disable-http", 87 | *s.cfg.ParentServiceType, 88 | *s.cfg.ParentType, 89 | *s.cfg.Parent, 90 | *s.cfg.CertFile, 91 | *s.cfg.KeyFile, 92 | *s.cfg.Timeout, 93 | *s.cfg.LocalSocks5Port, 94 | ) 95 | if *s.cfg.ParentKey != "" { 96 | sdkArgs += " -Z " + *s.cfg.ParentKey 97 | } 98 | if *s.cfg.ParentAuth != "" { 99 | sdkArgs += " -A " + *s.cfg.ParentAuth 100 | } 101 | if *s.cfg.CaCertFile != "" { 102 | sdkArgs += " --ca " + *s.cfg.CaCertFile 103 | } 104 | if *s.cfg.ParentCompress { 105 | sdkArgs += " -M" 106 | } 107 | s.log.Printf("start sps with : %s", sdkArgs) 108 | errStr := Start(s.serviceKey, sdkArgs) 109 | if errStr != "" { 110 | err = fmt.Errorf("start sps service fail,%s", errStr) 111 | } 112 | return 113 | } 114 | func (s *DNS) StopService() { 115 | defer func() { 116 | e := recover() 117 | if e != nil { 118 | s.log.Printf("stop dns service crashed,%s", e) 119 | } else { 120 | s.log.Printf("service dns stopped") 121 | } 122 | }() 123 | Stop(s.serviceKey) 124 | s.cache.Flush() 125 | s.exitSig <- true 126 | } 127 | func (s *DNS) Start(args interface{}, log *logger.Logger) (err error) { 128 | s.log = log 129 | s.cfg = args.(DNSArgs) 130 | if err = s.CheckArgs(); err != nil { 131 | return 132 | } 133 | if err = s.InitService(); err != nil { 134 | return 135 | } 136 | dns.HandleFunc(".", s.callback) 137 | go func() { 138 | log.Printf("dns server on udp %s", *s.cfg.Local) 139 | err := dns.ListenAndServe(*s.cfg.Local, "udp", nil) 140 | if err != nil { 141 | log.Printf("dns listen error: %s", err) 142 | } 143 | }() 144 | return 145 | } 146 | 147 | func (s *DNS) Clean() { 148 | s.StopService() 149 | } 150 | func (s *DNS) callback(w dns.ResponseWriter, req *dns.Msg) { 151 | defer func() { 152 | if err := recover(); err != nil { 153 | s.log.Printf("dns handler crashed with err : %s \nstack: %s", err, string(debug.Stack())) 154 | } 155 | }() 156 | var ( 157 | key string 158 | m *dns.Msg 159 | err error 160 | data []byte 161 | id uint16 162 | query []string 163 | questions []dns.Question 164 | ) 165 | if req.MsgHdr.Response == true { 166 | return 167 | } 168 | query = make([]string, len(req.Question)) 169 | for i, q := range req.Question { 170 | if q.Qtype != dns.TypeAAAA { 171 | questions = append(questions, q) 172 | } 173 | query[i] = fmt.Sprintf("(%s %s %s)", q.Name, dns.ClassToString[q.Qclass], dns.TypeToString[q.Qtype]) 174 | } 175 | 176 | if len(questions) == 0 { 177 | return 178 | } 179 | 180 | req.Question = questions 181 | id = req.Id 182 | req.Id = 0 183 | key = s.toMd5(req.String()) 184 | req.Id = id 185 | if reply, ok := s.cache.Get(key); ok { 186 | data, _ = reply.([]byte) 187 | } 188 | if data != nil && len(data) > 0 { 189 | m = &dns.Msg{} 190 | m.Unpack(data) 191 | m.Id = id 192 | err = w.WriteMsg(m) 193 | s.log.Printf("id: %5d cache: HIT %v", id, query) 194 | return 195 | 196 | } else { 197 | s.log.Printf("id: %5d cache: MISS %v", id, query) 198 | } 199 | 200 | s.log.Printf("id: %5d resolve: %v %s", id, query, *s.cfg.RemoteDNSAddress) 201 | 202 | rawConn, err := s.dialer.Dial("tcp", *s.cfg.RemoteDNSAddress) 203 | if err != nil { 204 | s.log.Printf("dail to %s fail,%s", *s.cfg.RemoteDNSAddress, err) 205 | return 206 | } 207 | defer rawConn.Close() 208 | co := new(dns.Conn) 209 | co.Conn = rawConn 210 | defer co.Close() 211 | if err = co.WriteMsg(req); err != nil { 212 | s.log.Printf("write dns query fail,%s", err) 213 | return 214 | } 215 | m, err = co.ReadMsg() 216 | if err == nil && m.Id != req.Id { 217 | s.log.Printf("id: %5d mismath", id) 218 | return 219 | } 220 | if err != nil || len(m.Answer) == 0 { 221 | s.log.Printf("dns query fail,%s", err) 222 | return 223 | } 224 | data, err = m.Pack() 225 | if err != nil { 226 | s.log.Printf("dns query fail,%s", err) 227 | return 228 | } 229 | 230 | _, err = w.Write(data) 231 | if err != nil { 232 | s.log.Printf("dns query fail,%s", err) 233 | return 234 | } 235 | m.Id = 0 236 | data, _ = m.Pack() 237 | ttl := 0 238 | if len(m.Answer) > 0 { 239 | if *s.cfg.DNSTTL > 0 { 240 | ttl = *s.cfg.DNSTTL 241 | } else { 242 | ttl = int(m.Answer[0].Header().Ttl) 243 | if ttl < 0 { 244 | ttl = *s.cfg.DNSTTL 245 | } 246 | } 247 | } 248 | s.cache.Set(key, data, time.Second*time.Duration(ttl)) 249 | m.Id = id 250 | s.log.Printf("id: %5d cache: CACHED %v TTL %v", id, query, ttl) 251 | } 252 | func (s *DNS) toMd5(data string) string { 253 | m := md5.New() 254 | m.Write([]byte(data)) 255 | return hex.EncodeToString(m.Sum(nil)) 256 | } 257 | -------------------------------------------------------------------------------- /lib/goproxy/sdk/android-ios/release_android.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | VER="v5.3" 3 | rm -rf sdk-android-*.tar.gz 4 | rm -rf android 5 | mkdir android 6 | 7 | #android ; jdk,android ndk & android sdk required, install gomobile go1.10 required 8 | #export GOPATH="$HOME/go" 9 | #export GOROOT="/usr/local/go" 10 | #export PATH="$GOROOT/bin:$GOPATH/bin:$PATH" 11 | #export ANDROID_HOME="$HOME/Android/Sdk" 12 | #export NDK_ROOT="$HOME/Android/Sdk/ndk-bundle" 13 | #export PATH="$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$NDK_ROOT:$PATH" 14 | #go get -v golang.org/x/mobile/cmd/gomobile 15 | #gomobile init 16 | 17 | gomobile bind -v -target=android -javapkg=snail007 -ldflags="-s -w" 18 | mv proxy.aar android/snail007.goproxy.sdk.aar 19 | mv proxy-sources.jar android/snail007.goproxy.sdk-sources.jar 20 | cp ../README.md android 21 | tar zcfv sdk-android-${VER}.tar.gz android 22 | rm -rf android 23 | 24 | echo "done." 25 | -------------------------------------------------------------------------------- /lib/goproxy/sdk/android-ios/release_ios.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | VER="v5.3" 3 | rm -rf sdk-ios-*.tar.gz 4 | rm -rf ios 5 | mkdir ios 6 | 7 | #ios XCode required 8 | gomobile bind -v -target=ios -ldflags="-s -w" 9 | mv Proxy.framework ios 10 | cp ../README.md ios 11 | tar zcfv sdk-ios-${VER}.tar.gz ios 12 | rm -rf ios 13 | 14 | echo "done." 15 | -------------------------------------------------------------------------------- /lib/goproxy/services/kcpcfg/args.go: -------------------------------------------------------------------------------- 1 | package kcpcfg 2 | 3 | import kcp "github.com/xtaci/kcp-go" 4 | 5 | type KCPConfigArgs struct { 6 | Key *string 7 | Crypt *string 8 | Mode *string 9 | MTU *int 10 | SndWnd *int 11 | RcvWnd *int 12 | DataShard *int 13 | ParityShard *int 14 | DSCP *int 15 | NoComp *bool 16 | AckNodelay *bool 17 | NoDelay *int 18 | Interval *int 19 | Resend *int 20 | NoCongestion *int 21 | SockBuf *int 22 | KeepAlive *int 23 | Block kcp.BlockCrypt 24 | } 25 | -------------------------------------------------------------------------------- /lib/goproxy/services/mux/mux_bridge.go: -------------------------------------------------------------------------------- 1 | package mux 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | services2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services" 7 | kcpcfg2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services/kcpcfg" 8 | utils2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils" 9 | "io" 10 | logger "log" 11 | "math/rand" 12 | "net" 13 | "strconv" 14 | "strings" 15 | "sync" 16 | "time" 17 | 18 | //"github.com/xtaci/smux" 19 | smux "github.com/hashicorp/yamux" 20 | ) 21 | 22 | const ( 23 | CONN_SERVER = uint8(4) 24 | CONN_CLIENT = uint8(5) 25 | ) 26 | 27 | type MuxBridgeArgs struct { 28 | CertFile *string 29 | KeyFile *string 30 | CertBytes []byte 31 | KeyBytes []byte 32 | Local *string 33 | LocalType *string 34 | Timeout *int 35 | IsCompress *bool 36 | KCP kcpcfg2.KCPConfigArgs 37 | } 38 | type MuxBridge struct { 39 | cfg MuxBridgeArgs 40 | clientControlConns utils2.ConcurrentMap 41 | serverConns utils2.ConcurrentMap 42 | router utils2.ClientKeyRouter 43 | l *sync.Mutex 44 | isStop bool 45 | sc *utils2.ServerChannel 46 | log *logger.Logger 47 | } 48 | 49 | func NewMuxBridge() services2.Service { 50 | b := &MuxBridge{ 51 | cfg: MuxBridgeArgs{}, 52 | clientControlConns: utils2.NewConcurrentMap(), 53 | serverConns: utils2.NewConcurrentMap(), 54 | l: &sync.Mutex{}, 55 | isStop: false, 56 | } 57 | b.router = utils2.NewClientKeyRouter(&b.clientControlConns, 50000) 58 | return b 59 | } 60 | 61 | func (s *MuxBridge) InitService() (err error) { 62 | return 63 | } 64 | func (s *MuxBridge) CheckArgs() (err error) { 65 | if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { 66 | err = fmt.Errorf("cert and key file required") 67 | return 68 | } 69 | if *s.cfg.LocalType == "tls" { 70 | s.cfg.CertBytes, s.cfg.KeyBytes, err = utils2.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) 71 | if err != nil { 72 | return 73 | } 74 | } 75 | return 76 | } 77 | func (s *MuxBridge) StopService() { 78 | defer func() { 79 | e := recover() 80 | if e != nil { 81 | s.log.Printf("stop bridge service crashed,%s", e) 82 | } else { 83 | s.log.Printf("service bridge stopped") 84 | } 85 | }() 86 | s.isStop = true 87 | if s.sc != nil && (*s.sc).Listener != nil { 88 | (*(*s.sc).Listener).Close() 89 | } 90 | for _, g := range s.clientControlConns.Items() { 91 | for _, session := range g.(*utils2.ConcurrentMap).Items() { 92 | (session.(*smux.Session)).Close() 93 | } 94 | } 95 | for _, c := range s.serverConns.Items() { 96 | (*c.(*net.Conn)).Close() 97 | } 98 | } 99 | func (s *MuxBridge) Start(args interface{}, log *logger.Logger) (err error) { 100 | s.log = log 101 | s.cfg = args.(MuxBridgeArgs) 102 | if err = s.CheckArgs(); err != nil { 103 | return 104 | } 105 | if err = s.InitService(); err != nil { 106 | return 107 | } 108 | 109 | host, port, _ := net.SplitHostPort(*s.cfg.Local) 110 | p, _ := strconv.Atoi(port) 111 | sc := utils2.NewServerChannel(host, p, s.log) 112 | if *s.cfg.LocalType == "tcp" { 113 | err = sc.ListenTCP(s.handler) 114 | } else if *s.cfg.LocalType == "tls" { 115 | err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.handler) 116 | } else if *s.cfg.LocalType == "kcp" { 117 | err = sc.ListenKCP(s.cfg.KCP, s.handler, s.log) 118 | } 119 | if err != nil { 120 | return 121 | } 122 | s.sc = &sc 123 | s.log.Printf("%s bridge on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) 124 | return 125 | } 126 | func (s *MuxBridge) Clean() { 127 | s.StopService() 128 | } 129 | func (s *MuxBridge) handler(inConn net.Conn) { 130 | reader := bufio.NewReader(inConn) 131 | 132 | var err error 133 | var connType uint8 134 | var key string 135 | inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 136 | err = utils2.ReadPacket(reader, &connType, &key) 137 | inConn.SetDeadline(time.Time{}) 138 | if err != nil { 139 | s.log.Printf("read error,ERR:%s", err) 140 | return 141 | } 142 | switch connType { 143 | case CONN_SERVER: 144 | var serverID string 145 | inAddr := inConn.RemoteAddr().String() 146 | inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 147 | err = utils2.ReadPacketData(reader, &serverID) 148 | inConn.SetDeadline(time.Time{}) 149 | if err != nil { 150 | s.log.Printf("read error,ERR:%s", err) 151 | return 152 | } 153 | s.log.Printf("server connection %s %s connected", serverID, key) 154 | if c, ok := s.serverConns.Get(inAddr); ok { 155 | (*c.(*net.Conn)).Close() 156 | } 157 | s.serverConns.Set(inAddr, &inConn) 158 | session, err := smux.Server(inConn, nil) 159 | if err != nil { 160 | utils2.CloseConn(&inConn) 161 | s.log.Printf("server session error,ERR:%s", err) 162 | return 163 | } 164 | for { 165 | if s.isStop { 166 | return 167 | } 168 | stream, err := session.AcceptStream() 169 | if err != nil { 170 | session.Close() 171 | utils2.CloseConn(&inConn) 172 | s.serverConns.Remove(inAddr) 173 | s.log.Printf("server connection %s %s released", serverID, key) 174 | return 175 | } 176 | go func() { 177 | defer func() { 178 | if e := recover(); e != nil { 179 | s.log.Printf("bridge callback crashed,err: %s", e) 180 | } 181 | }() 182 | s.callback(stream, serverID, key) 183 | }() 184 | } 185 | case CONN_CLIENT: 186 | s.log.Printf("client connection %s connected", key) 187 | session, err := smux.Client(inConn, nil) 188 | if err != nil { 189 | utils2.CloseConn(&inConn) 190 | s.log.Printf("client session error,ERR:%s", err) 191 | return 192 | } 193 | keyInfo := strings.Split(key, "-") 194 | if len(keyInfo) != 2 { 195 | utils2.CloseConn(&inConn) 196 | s.log.Printf("client key format error,key:%s", key) 197 | return 198 | } 199 | groupKey := keyInfo[0] 200 | index := keyInfo[1] 201 | s.l.Lock() 202 | defer s.l.Unlock() 203 | if !s.clientControlConns.Has(groupKey) { 204 | item := utils2.NewConcurrentMap() 205 | s.clientControlConns.Set(groupKey, &item) 206 | } 207 | _group, _ := s.clientControlConns.Get(groupKey) 208 | group := _group.(*utils2.ConcurrentMap) 209 | if v, ok := group.Get(index); ok { 210 | v.(*smux.Session).Close() 211 | } 212 | group.Set(index, session) 213 | // s.clientControlConns.Set(key, session) 214 | go func() { 215 | for { 216 | if s.isStop { 217 | return 218 | } 219 | if session.IsClosed() { 220 | s.l.Lock() 221 | defer s.l.Unlock() 222 | if sess, ok := group.Get(index); ok && sess.(*smux.Session).IsClosed() { 223 | group.Remove(index) 224 | s.log.Printf("client connection %s released", key) 225 | } 226 | if group.IsEmpty() { 227 | s.clientControlConns.Remove(groupKey) 228 | } 229 | break 230 | } 231 | time.Sleep(time.Second * 5) 232 | } 233 | }() 234 | //s.log.Printf("set client session,key: %s", key) 235 | } 236 | 237 | } 238 | func (s *MuxBridge) callback(inConn net.Conn, serverID, key string) { 239 | try := 20 240 | for { 241 | if s.isStop { 242 | return 243 | } 244 | try-- 245 | if try == 0 { 246 | break 247 | } 248 | if key == "*" { 249 | key = s.router.GetKey() 250 | } 251 | _group, ok := s.clientControlConns.Get(key) 252 | if !ok { 253 | s.log.Printf("client %s session not exists for server stream %s, retrying...", key, serverID) 254 | time.Sleep(time.Second * 3) 255 | continue 256 | } 257 | group := _group.(*utils2.ConcurrentMap) 258 | keys := group.Keys() 259 | keysLen := len(keys) 260 | i := 0 261 | if keysLen > 0 { 262 | i = rand.Intn(keysLen) 263 | } else { 264 | s.log.Printf("client %s session empty for server stream %s, retrying...", key, serverID) 265 | time.Sleep(time.Second * 3) 266 | continue 267 | } 268 | index := keys[i] 269 | s.log.Printf("select client : %s-%s", key, index) 270 | session, _ := group.Get(index) 271 | //session.(*smux.Session).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 272 | stream, err := session.(*smux.Session).OpenStream() 273 | //session.(*smux.Session).SetDeadline(time.Time{}) 274 | if err != nil { 275 | s.log.Printf("%s client session open stream %s fail, err: %s, retrying...", key, serverID, err) 276 | time.Sleep(time.Second * 3) 277 | continue 278 | } else { 279 | s.log.Printf("stream %s -> %s created", serverID, key) 280 | die1 := make(chan bool, 1) 281 | die2 := make(chan bool, 1) 282 | go func() { 283 | io.Copy(stream, inConn) 284 | die1 <- true 285 | }() 286 | go func() { 287 | io.Copy(inConn, stream) 288 | die2 <- true 289 | }() 290 | select { 291 | case <-die1: 292 | case <-die2: 293 | } 294 | stream.Close() 295 | inConn.Close() 296 | s.log.Printf("%s server %s stream released", key, serverID) 297 | break 298 | } 299 | } 300 | 301 | } 302 | -------------------------------------------------------------------------------- /lib/goproxy/services/mux/mux_client.go: -------------------------------------------------------------------------------- 1 | package mux 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | services2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services" 7 | kcpcfg2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services/kcpcfg" 8 | utils2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils" 9 | "io" 10 | logger "log" 11 | "net" 12 | "time" 13 | 14 | "github.com/golang/snappy" 15 | //"github.com/xtaci/smux" 16 | smux "github.com/hashicorp/yamux" 17 | ) 18 | 19 | type MuxClientArgs struct { 20 | Parent *string 21 | ParentType *string 22 | CertFile *string 23 | KeyFile *string 24 | CertBytes []byte 25 | KeyBytes []byte 26 | Key *string 27 | Timeout *int 28 | IsCompress *bool 29 | SessionCount *int 30 | KCP kcpcfg2.KCPConfigArgs 31 | } 32 | type MuxClient struct { 33 | cfg MuxClientArgs 34 | isStop bool 35 | sessions utils2.ConcurrentMap 36 | log *logger.Logger 37 | } 38 | 39 | func NewMuxClient() services2.Service { 40 | return &MuxClient{ 41 | cfg: MuxClientArgs{}, 42 | isStop: false, 43 | sessions: utils2.NewConcurrentMap(), 44 | } 45 | } 46 | 47 | func (s *MuxClient) InitService() (err error) { 48 | return 49 | } 50 | 51 | func (s *MuxClient) CheckArgs() (err error) { 52 | if *s.cfg.Parent != "" { 53 | s.log.Printf("use tls parent %s", *s.cfg.Parent) 54 | } else { 55 | err = fmt.Errorf("parent required") 56 | return 57 | } 58 | if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { 59 | err = fmt.Errorf("cert and key file required") 60 | return 61 | } 62 | if *s.cfg.ParentType == "tls" { 63 | s.cfg.CertBytes, s.cfg.KeyBytes, err = utils2.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) 64 | if err != nil { 65 | return 66 | } 67 | } 68 | return 69 | } 70 | func (s *MuxClient) StopService() { 71 | defer func() { 72 | e := recover() 73 | if e != nil { 74 | s.log.Printf("stop client service crashed,%s", e) 75 | } else { 76 | s.log.Printf("service client stopped") 77 | } 78 | }() 79 | s.isStop = true 80 | for _, sess := range s.sessions.Items() { 81 | sess.(*smux.Session).Close() 82 | } 83 | } 84 | func (s *MuxClient) Start(args interface{}, log *logger.Logger) (err error) { 85 | s.log = log 86 | s.cfg = args.(MuxClientArgs) 87 | if err = s.CheckArgs(); err != nil { 88 | return 89 | } 90 | if err = s.InitService(); err != nil { 91 | return 92 | } 93 | s.log.Printf("client started") 94 | count := 1 95 | if *s.cfg.SessionCount > 0 { 96 | count = *s.cfg.SessionCount 97 | } 98 | for i := 1; i <= count; i++ { 99 | key := fmt.Sprintf("worker[%d]", i) 100 | s.log.Printf("session %s started", key) 101 | go func(i int) { 102 | defer func() { 103 | e := recover() 104 | if e != nil { 105 | s.log.Printf("session worker crashed: %s", e) 106 | } 107 | }() 108 | for { 109 | if s.isStop { 110 | return 111 | } 112 | conn, err := s.getParentConn() 113 | if err != nil { 114 | s.log.Printf("connection err: %s, retrying...", err) 115 | time.Sleep(time.Second * 3) 116 | continue 117 | } 118 | conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 119 | _, err = conn.Write(utils2.BuildPacket(CONN_CLIENT, fmt.Sprintf("%s-%d", *s.cfg.Key, i))) 120 | conn.SetDeadline(time.Time{}) 121 | if err != nil { 122 | conn.Close() 123 | s.log.Printf("connection err: %s, retrying...", err) 124 | time.Sleep(time.Second * 3) 125 | continue 126 | } 127 | session, err := smux.Server(conn, nil) 128 | if err != nil { 129 | s.log.Printf("session err: %s, retrying...", err) 130 | conn.Close() 131 | time.Sleep(time.Second * 3) 132 | continue 133 | } 134 | if _sess, ok := s.sessions.Get(key); ok { 135 | _sess.(*smux.Session).Close() 136 | } 137 | s.sessions.Set(key, session) 138 | for { 139 | if s.isStop { 140 | return 141 | } 142 | stream, err := session.AcceptStream() 143 | if err != nil { 144 | s.log.Printf("accept stream err: %s, retrying...", err) 145 | session.Close() 146 | time.Sleep(time.Second * 3) 147 | break 148 | } 149 | go func() { 150 | defer func() { 151 | e := recover() 152 | if e != nil { 153 | s.log.Printf("stream handler crashed: %s", e) 154 | } 155 | }() 156 | var ID, clientLocalAddr, serverID string 157 | stream.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 158 | err = utils2.ReadPacketData(stream, &ID, &clientLocalAddr, &serverID) 159 | stream.SetDeadline(time.Time{}) 160 | if err != nil { 161 | s.log.Printf("read stream signal err: %s", err) 162 | stream.Close() 163 | return 164 | } 165 | s.log.Printf("worker[%d] signal revecived,server %s stream %s %s", i, serverID, ID, clientLocalAddr) 166 | protocol := clientLocalAddr[:3] 167 | localAddr := clientLocalAddr[4:] 168 | if protocol == "udp" { 169 | s.ServeUDP(stream, localAddr, ID) 170 | } else { 171 | s.ServeConn(stream, localAddr, ID) 172 | } 173 | }() 174 | } 175 | } 176 | }(i) 177 | } 178 | return 179 | } 180 | func (s *MuxClient) Clean() { 181 | s.StopService() 182 | } 183 | func (s *MuxClient) getParentConn() (conn net.Conn, err error) { 184 | if *s.cfg.ParentType == "tls" { 185 | var _conn tls.Conn 186 | _conn, err = utils2.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) 187 | if err == nil { 188 | conn = net.Conn(&_conn) 189 | } 190 | } else if *s.cfg.ParentType == "kcp" { 191 | conn, err = utils2.ConnectKCPHost(*s.cfg.Parent, s.cfg.KCP) 192 | } else { 193 | conn, err = utils2.ConnectHost(*s.cfg.Parent, *s.cfg.Timeout) 194 | } 195 | return 196 | } 197 | func (s *MuxClient) ServeUDP(inConn *smux.Stream, localAddr, ID string) { 198 | 199 | for { 200 | if s.isStop { 201 | return 202 | } 203 | inConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 204 | srcAddr, body, err := utils2.ReadUDPPacket(inConn) 205 | inConn.SetDeadline(time.Time{}) 206 | if err != nil { 207 | s.log.Printf("udp packet revecived fail, err: %s", err) 208 | s.log.Printf("connection %s released", ID) 209 | inConn.Close() 210 | break 211 | } else { 212 | //s.log.Printf("udp packet revecived:%s,%v", srcAddr, body) 213 | go func() { 214 | defer func() { 215 | if e := recover(); e != nil { 216 | s.log.Printf("client processUDPPacket crashed,err: %s", e) 217 | } 218 | }() 219 | s.processUDPPacket(inConn, srcAddr, localAddr, body) 220 | }() 221 | 222 | } 223 | 224 | } 225 | // } 226 | } 227 | func (s *MuxClient) processUDPPacket(inConn *smux.Stream, srcAddr, localAddr string, body []byte) { 228 | dstAddr, err := net.ResolveUDPAddr("udp", localAddr) 229 | if err != nil { 230 | s.log.Printf("can't resolve address: %s", err) 231 | inConn.Close() 232 | return 233 | } 234 | clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} 235 | conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) 236 | if err != nil { 237 | s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) 238 | return 239 | } 240 | conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 241 | _, err = conn.Write(body) 242 | conn.SetDeadline(time.Time{}) 243 | if err != nil { 244 | s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) 245 | return 246 | } 247 | //s.log.Printf("send udp packet to %s success", dstAddr.String()) 248 | buf := make([]byte, 1024) 249 | conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 250 | length, _, err := conn.ReadFromUDP(buf) 251 | conn.SetDeadline(time.Time{}) 252 | if err != nil { 253 | s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) 254 | return 255 | } 256 | respBody := buf[0:length] 257 | //s.log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody) 258 | bs := utils2.UDPPacket(srcAddr, respBody) 259 | (*inConn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 260 | _, err = (*inConn).Write(bs) 261 | (*inConn).SetDeadline(time.Time{}) 262 | if err != nil { 263 | s.log.Printf("send udp response fail ,ERR:%s", err) 264 | inConn.Close() 265 | return 266 | } 267 | //s.log.Printf("send udp response success ,from:%s ,%d ,%v", dstAddr.String(), len(bs), bs) 268 | } 269 | func (s *MuxClient) ServeConn(inConn *smux.Stream, localAddr, ID string) { 270 | var err error 271 | var outConn net.Conn 272 | i := 0 273 | for { 274 | if s.isStop { 275 | return 276 | } 277 | i++ 278 | outConn, err = utils2.ConnectHost(localAddr, *s.cfg.Timeout) 279 | if err == nil || i == 3 { 280 | break 281 | } else { 282 | if i == 3 { 283 | s.log.Printf("connect to %s err: %s, retrying...", localAddr, err) 284 | time.Sleep(2 * time.Second) 285 | continue 286 | } 287 | } 288 | } 289 | if err != nil { 290 | inConn.Close() 291 | utils2.CloseConn(&outConn) 292 | s.log.Printf("build connection error, err: %s", err) 293 | return 294 | } 295 | 296 | s.log.Printf("stream %s created", ID) 297 | if *s.cfg.IsCompress { 298 | die1 := make(chan bool, 1) 299 | die2 := make(chan bool, 1) 300 | go func() { 301 | io.Copy(outConn, snappy.NewReader(inConn)) 302 | die1 <- true 303 | }() 304 | go func() { 305 | io.Copy(snappy.NewWriter(inConn), outConn) 306 | die2 <- true 307 | }() 308 | select { 309 | case <-die1: 310 | case <-die2: 311 | } 312 | outConn.Close() 313 | inConn.Close() 314 | s.log.Printf("%s stream %s released", *s.cfg.Key, ID) 315 | } else { 316 | utils2.IoBind(inConn, outConn, func(err interface{}) { 317 | s.log.Printf("stream %s released", ID) 318 | }, s.log) 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /lib/goproxy/services/service.go: -------------------------------------------------------------------------------- 1 | package services 2 | 3 | import ( 4 | "fmt" 5 | logger "log" 6 | "runtime/debug" 7 | ) 8 | 9 | type Service interface { 10 | Start(args interface{}, log *logger.Logger) (err error) 11 | Clean() 12 | } 13 | type ServiceItem struct { 14 | S Service 15 | Args interface{} 16 | Name string 17 | Log *logger.Logger 18 | } 19 | 20 | var servicesMap = map[string]*ServiceItem{} 21 | 22 | func Regist(name string, s Service, args interface{}, log *logger.Logger) { 23 | Stop(name) 24 | servicesMap[name] = &ServiceItem{ 25 | S: s, 26 | Args: args, 27 | Name: name, 28 | Log: log, 29 | } 30 | } 31 | func GetService(name string) *ServiceItem { 32 | if s, ok := servicesMap[name]; ok && s.S != nil { 33 | return s 34 | } 35 | return nil 36 | 37 | } 38 | func Stop(name string) { 39 | if s, ok := servicesMap[name]; ok && s.S != nil { 40 | s.S.Clean() 41 | } 42 | } 43 | func Run(name string, args interface{}) (service *ServiceItem, err error) { 44 | service, ok := servicesMap[name] 45 | if ok { 46 | defer func() { 47 | e := recover() 48 | if e != nil { 49 | err = fmt.Errorf("%s servcie crashed, ERR: %s\ntrace:%s", name, e, string(debug.Stack())) 50 | } 51 | }() 52 | if args != nil { 53 | err = service.S.Start(args, service.Log) 54 | } else { 55 | err = service.S.Start(service.Args, service.Log) 56 | } 57 | if err != nil { 58 | err = fmt.Errorf("%s servcie fail, ERR: %s", name, err) 59 | } 60 | } else { 61 | err = fmt.Errorf("service %s not found", name) 62 | } 63 | return 64 | } 65 | -------------------------------------------------------------------------------- /lib/goproxy/services/socks/udp.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | utils2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils" 7 | goaes2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils/aes" 8 | socks2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils/socks" 9 | "net" 10 | "runtime/debug" 11 | "strconv" 12 | "strings" 13 | "time" 14 | ) 15 | 16 | func (s *Socks) ParentUDPKey() (key []byte) { 17 | switch *s.cfg.ParentType { 18 | case "tcp": 19 | if *s.cfg.ParentKey != "" { 20 | v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey))) 21 | return []byte(v)[:24] 22 | } 23 | case "tls": 24 | return s.cfg.KeyBytes[:24] 25 | case "kcp": 26 | v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) 27 | return []byte(v)[:24] 28 | } 29 | return 30 | } 31 | func (s *Socks) LocalUDPKey() (key []byte) { 32 | switch *s.cfg.LocalType { 33 | case "tcp": 34 | if *s.cfg.LocalKey != "" { 35 | v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey))) 36 | return []byte(v)[:24] 37 | } 38 | case "tls": 39 | return s.cfg.KeyBytes[:24] 40 | case "kcp": 41 | v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) 42 | return []byte(v)[:24] 43 | } 44 | return 45 | } 46 | func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks2.MethodsRequest, request socks2.Request) { 47 | defer func() { 48 | if e := recover(); e != nil { 49 | s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack())) 50 | } 51 | }() 52 | if *s.cfg.ParentType == "ssh" { 53 | utils2.CloseConn(inConn) 54 | return 55 | } 56 | srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String()) 57 | inconnRemoteAddr := (*inConn).RemoteAddr().String() 58 | localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} 59 | udpListener, err := net.ListenUDP("udp", localAddr) 60 | if err != nil { 61 | (*inConn).Close() 62 | udpListener.Close() 63 | s.log.Printf("udp bind fail , %s", err) 64 | return 65 | } 66 | host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) 67 | _, port, _ := net.SplitHostPort(udpListener.LocalAddr().String()) 68 | if len(*s.cfg.LocalIPS) > 0 { 69 | host = (*s.cfg.LocalIPS)[0] 70 | } 71 | s.log.Printf("proxy udp on %s , for %s", net.JoinHostPort(host, port), inconnRemoteAddr) 72 | request.UDPReply(socks2.REP_SUCCESS, net.JoinHostPort(host, port)) 73 | s.userConns.Set(inconnRemoteAddr, inConn) 74 | var ( 75 | outUDPConn *net.UDPConn 76 | outconn net.Conn 77 | outconnLocalAddr string 78 | isClosedErr = func(err error) bool { 79 | return err != nil && strings.Contains(err.Error(), "use of closed network connection") 80 | } 81 | destAddr *net.UDPAddr 82 | ) 83 | var clean = func(msg, err string) { 84 | raddr := "" 85 | if outUDPConn != nil { 86 | raddr = outUDPConn.RemoteAddr().String() 87 | outUDPConn.Close() 88 | } 89 | if msg != "" { 90 | if raddr != "" { 91 | s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr) 92 | } else { 93 | s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr) 94 | } 95 | } 96 | (*inConn).Close() 97 | udpListener.Close() 98 | s.userConns.Remove(inconnRemoteAddr) 99 | if outconn != nil { 100 | outconn.Close() 101 | } 102 | if outconnLocalAddr != "" { 103 | s.userConns.Remove(outconnLocalAddr) 104 | } 105 | } 106 | defer clean("", "") 107 | go func() { 108 | defer func() { 109 | if e := recover(); e != nil { 110 | s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) 111 | } 112 | }() 113 | buf := make([]byte, 1) 114 | (*inConn).SetReadDeadline(time.Time{}) 115 | if _, err := (*inConn).Read(buf); err != nil { 116 | clean("udp related tcp conn disconnected with read", err.Error()) 117 | } 118 | }() 119 | go func() { 120 | defer func() { 121 | if e := recover(); e != nil { 122 | s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack())) 123 | } 124 | }() 125 | for { 126 | (*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5)) 127 | if _, err := (*inConn).Write([]byte{0x00}); err != nil { 128 | clean("udp related tcp conn disconnected with write", err.Error()) 129 | return 130 | } 131 | (*inConn).SetWriteDeadline(time.Time{}) 132 | time.Sleep(time.Second * 5) 133 | } 134 | }() 135 | useProxy := true 136 | if *s.cfg.Parent != "" { 137 | dstHost, _, _ := net.SplitHostPort(request.Addr()) 138 | if utils2.IsIternalIP(dstHost, *s.cfg.Always) { 139 | useProxy = false 140 | } else { 141 | var isInMap bool 142 | useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr()) 143 | if !isInMap { 144 | s.checker.Add(request.Addr(), s.Resolve(request.Addr())) 145 | } 146 | } 147 | } else { 148 | useProxy = false 149 | } 150 | if useProxy { 151 | //parent proxy 152 | outconn, err := s.getOutConn(nil, nil, "", false) 153 | if err != nil { 154 | clean("connnect fail", fmt.Sprintf("%s", err)) 155 | return 156 | } 157 | client := socks2.NewClientConn(&outconn, "udp", request.Addr(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) 158 | if err = client.Handshake(); err != nil { 159 | clean("handshake fail", fmt.Sprintf("%s", err)) 160 | return 161 | } 162 | //outconnRemoteAddr := outconn.RemoteAddr().String() 163 | outconnLocalAddr = outconn.LocalAddr().String() 164 | s.userConns.Set(outconnLocalAddr, &outconn) 165 | go func() { 166 | defer func() { 167 | if e := recover(); e != nil { 168 | s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) 169 | } 170 | }() 171 | buf := make([]byte, 1) 172 | outconn.SetReadDeadline(time.Time{}) 173 | if _, err := outconn.Read(buf); err != nil { 174 | clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err)) 175 | } 176 | }() 177 | //forward to parent udp 178 | //s.log.Printf("parent udp address %s", client.UDPAddr) 179 | destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) 180 | } 181 | s.log.Printf("use proxy %v : udp %s", useProxy, request.Addr()) 182 | //relay 183 | for { 184 | buf := utils2.LeakyBuffer.Get() 185 | defer utils2.LeakyBuffer.Put(buf) 186 | n, srcAddr, err := udpListener.ReadFromUDP(buf) 187 | if err != nil { 188 | s.log.Printf("udp listener read fail, %s", err.Error()) 189 | if isClosedErr(err) { 190 | return 191 | } 192 | continue 193 | } 194 | srcIP0, _, _ := net.SplitHostPort(srcAddr.String()) 195 | //IP not match drop it 196 | if srcIP != srcIP0 { 197 | continue 198 | } 199 | p := socks2.NewPacketUDP() 200 | //convert data to raw 201 | if len(s.udpLocalKey) > 0 { 202 | var v []byte 203 | v, err = goaes2.Decrypt(s.udpLocalKey, buf[:n]) 204 | if err == nil { 205 | err = p.Parse(v) 206 | } 207 | } else { 208 | err = p.Parse(buf[:n]) 209 | } 210 | //err = p.Parse(buf[:n]) 211 | if err != nil { 212 | s.log.Printf("udp listener parse packet fail, %s", err.Error()) 213 | continue 214 | } 215 | 216 | port, _ := strconv.Atoi(p.Port()) 217 | 218 | if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { 219 | if destAddr == nil { 220 | destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port} 221 | } 222 | outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) 223 | if err != nil { 224 | s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) 225 | continue 226 | } 227 | s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) 228 | go func() { 229 | defer func() { 230 | if e := recover(); e != nil { 231 | s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack())) 232 | } 233 | }() 234 | defer s.udpRelatedPacketConns.Remove(srcAddr.String()) 235 | //out->local io copy 236 | buf := utils2.LeakyBuffer.Get() 237 | defer utils2.LeakyBuffer.Put(buf) 238 | for { 239 | n, err := outUDPConn.Read(buf) 240 | if err != nil { 241 | s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr) 242 | if isClosedErr(err) { 243 | return 244 | } 245 | continue 246 | } 247 | //var dlen = n 248 | if useProxy { 249 | //forward to local 250 | var v []byte 251 | //convert parent data to raw 252 | if len(s.udpParentKey) > 0 { 253 | v, err = goaes2.Decrypt(s.udpParentKey, buf[:n]) 254 | if err != nil { 255 | s.log.Printf("udp outconn parse packet fail, %s", err.Error()) 256 | continue 257 | } 258 | } else { 259 | v = buf[:n] 260 | } 261 | //now v is raw, try convert v to local 262 | if len(s.udpLocalKey) > 0 { 263 | v, _ = goaes2.Encrypt(s.udpLocalKey, v) 264 | } 265 | _, err = udpListener.WriteTo(v, srcAddr) 266 | // _, err = udpListener.WriteTo(buf[:n], srcAddr) 267 | } else { 268 | rp := socks2.NewPacketUDP() 269 | rp.Build(destAddr.String(), buf[:n]) 270 | v := rp.Bytes() 271 | //dlen = len(v) 272 | //rp.Bytes() v is raw, try convert to local 273 | if len(s.udpLocalKey) > 0 { 274 | v, _ = goaes2.Encrypt(s.udpLocalKey, v) 275 | } 276 | _, err = udpListener.WriteTo(v, srcAddr) 277 | } 278 | 279 | if err != nil { 280 | s.udpRelatedPacketConns.Remove(srcAddr.String()) 281 | s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) 282 | if isClosedErr(err) { 283 | return 284 | } 285 | continue 286 | } else { 287 | //s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr) 288 | } 289 | } 290 | }() 291 | } else { 292 | outUDPConn = v.(*net.UDPConn) 293 | } 294 | //local->out io copy 295 | if useProxy { 296 | //forward to parent 297 | //p is raw, now convert it to parent 298 | var v []byte 299 | if len(s.udpParentKey) > 0 { 300 | v, _ = goaes2.Encrypt(s.udpParentKey, p.Bytes()) 301 | } else { 302 | v = p.Bytes() 303 | } 304 | _, err = outUDPConn.Write(v) 305 | // _, err = outUDPConn.Write(p.Bytes()) 306 | } else { 307 | _, err = outUDPConn.Write(p.Data()) 308 | } 309 | if err != nil { 310 | if isClosedErr(err) { 311 | return 312 | } 313 | s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr) 314 | continue 315 | } else { 316 | //s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr) 317 | } 318 | } 319 | 320 | } 321 | -------------------------------------------------------------------------------- /lib/goproxy/services/sps/socksudp.go: -------------------------------------------------------------------------------- 1 | package sps 2 | 3 | import ( 4 | "crypto/md5" 5 | "fmt" 6 | utils2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils" 7 | goaes2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils/aes" 8 | conncrypt2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils/conncrypt" 9 | socks2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils/socks" 10 | "net" 11 | "runtime/debug" 12 | "strconv" 13 | "strings" 14 | "time" 15 | ) 16 | 17 | func (s *SPS) ParentUDPKey() (key []byte) { 18 | switch *s.cfg.ParentType { 19 | case "tcp": 20 | if *s.cfg.ParentKey != "" { 21 | v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey))) 22 | return []byte(v)[:24] 23 | } 24 | case "tls": 25 | return s.cfg.KeyBytes[:24] 26 | case "kcp": 27 | v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) 28 | return []byte(v)[:24] 29 | } 30 | return 31 | } 32 | func (s *SPS) LocalUDPKey() (key []byte) { 33 | switch *s.cfg.LocalType { 34 | case "tcp": 35 | if *s.cfg.LocalKey != "" { 36 | v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey))) 37 | return []byte(v)[:24] 38 | } 39 | case "tls": 40 | return s.cfg.KeyBytes[:24] 41 | case "kcp": 42 | v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) 43 | return []byte(v)[:24] 44 | } 45 | return 46 | } 47 | func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks2.ServerConn) { 48 | defer func() { 49 | if e := recover(); e != nil { 50 | s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack())) 51 | } 52 | }() 53 | if *s.cfg.ParentType == "ssh" { 54 | utils2.CloseConn(inConn) 55 | return 56 | } 57 | srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String()) 58 | inconnRemoteAddr := (*inConn).RemoteAddr().String() 59 | localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} 60 | udpListener := serverConn.UDPConnListener 61 | s.log.Printf("proxy udp on %s , for %s", udpListener.LocalAddr(), inconnRemoteAddr) 62 | s.userConns.Set(inconnRemoteAddr, inConn) 63 | var ( 64 | outUDPConn *net.UDPConn 65 | outconn net.Conn 66 | outconnLocalAddr string 67 | isClosedErr = func(err error) bool { 68 | return err != nil && strings.Contains(err.Error(), "use of closed network connection") 69 | } 70 | destAddr *net.UDPAddr 71 | ) 72 | var clean = func(msg, err string) { 73 | raddr := "" 74 | if outUDPConn != nil { 75 | raddr = outUDPConn.RemoteAddr().String() 76 | outUDPConn.Close() 77 | } 78 | if msg != "" { 79 | if raddr != "" { 80 | s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr) 81 | } else { 82 | s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr) 83 | } 84 | } 85 | (*inConn).Close() 86 | udpListener.Close() 87 | s.userConns.Remove(inconnRemoteAddr) 88 | if outconn != nil { 89 | outconn.Close() 90 | } 91 | if outconnLocalAddr != "" { 92 | s.userConns.Remove(outconnLocalAddr) 93 | } 94 | } 95 | defer clean("", "") 96 | go func() { 97 | defer func() { 98 | if e := recover(); e != nil { 99 | s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) 100 | } 101 | }() 102 | buf := make([]byte, 1) 103 | (*inConn).SetReadDeadline(time.Time{}) 104 | if _, err := (*inConn).Read(buf); err != nil { 105 | clean("udp related tcp conn disconnected with read", err.Error()) 106 | } 107 | }() 108 | go func() { 109 | defer func() { 110 | if e := recover(); e != nil { 111 | s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack())) 112 | } 113 | }() 114 | for { 115 | (*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5)) 116 | if _, err := (*inConn).Write([]byte{0x00}); err != nil { 117 | clean("udp related tcp conn disconnected with write", err.Error()) 118 | return 119 | } 120 | (*inConn).SetWriteDeadline(time.Time{}) 121 | time.Sleep(time.Second * 5) 122 | } 123 | }() 124 | //parent proxy 125 | outconn, err := s.outPool.Get() 126 | //outconn, err := s.GetParentConn(nil, nil, "", false) 127 | if err != nil { 128 | clean("connnect fail", fmt.Sprintf("%s", err)) 129 | return 130 | } 131 | if *s.cfg.ParentCompress { 132 | outconn = utils2.NewCompConn(outconn) 133 | } 134 | if *s.cfg.ParentKey != "" { 135 | outconn = conncrypt2.New(outconn, &conncrypt2.Config{ 136 | Password: *s.cfg.ParentKey, 137 | }) 138 | } 139 | 140 | s.log.Printf("connect %s for udp", serverConn.Target()) 141 | //socks client 142 | var client *socks2.ClientConn 143 | auth := serverConn.AuthData() 144 | if *s.cfg.ParentAuth != "" { 145 | a := strings.Split(*s.cfg.ParentAuth, ":") 146 | if len(a) != 2 { 147 | err = fmt.Errorf("parent auth data format error") 148 | return 149 | } 150 | client = socks2.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &socks2.Auth{User: a[0], Password: a[1]}, nil) 151 | } else { 152 | if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" { 153 | client = socks2.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, nil) 154 | } else { 155 | client = socks2.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) 156 | } 157 | } 158 | 159 | if err = client.Handshake(); err != nil { 160 | clean("handshake fail", fmt.Sprintf("%s", err)) 161 | return 162 | } 163 | 164 | //outconnRemoteAddr := outconn.RemoteAddr().String() 165 | outconnLocalAddr = outconn.LocalAddr().String() 166 | s.userConns.Set(outconnLocalAddr, &outconn) 167 | go func() { 168 | defer func() { 169 | if e := recover(); e != nil { 170 | s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) 171 | } 172 | }() 173 | buf := make([]byte, 1) 174 | outconn.SetReadDeadline(time.Time{}) 175 | if _, err := outconn.Read(buf); err != nil { 176 | clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err)) 177 | } 178 | }() 179 | //forward to parent udp 180 | //s.log.Printf("parent udp address %s", client.UDPAddr) 181 | destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) 182 | //relay 183 | buf := utils2.LeakyBuffer.Get() 184 | defer utils2.LeakyBuffer.Put(buf) 185 | for { 186 | n, srcAddr, err := udpListener.ReadFromUDP(buf) 187 | if err != nil { 188 | s.log.Printf("udp listener read fail, %s", err.Error()) 189 | if isClosedErr(err) { 190 | return 191 | } 192 | continue 193 | } 194 | srcIP0, _, _ := net.SplitHostPort(srcAddr.String()) 195 | //IP not match drop it 196 | if srcIP != srcIP0 { 197 | continue 198 | } 199 | p := socks2.NewPacketUDP() 200 | //convert data to raw 201 | if len(s.udpLocalKey) > 0 { 202 | var v []byte 203 | v, err = goaes2.Decrypt(s.udpLocalKey, buf[:n]) 204 | if err == nil { 205 | err = p.Parse(v) 206 | } 207 | } else { 208 | err = p.Parse(buf[:n]) 209 | } 210 | if err != nil { 211 | s.log.Printf("udp listener parse packet fail, %s", err.Error()) 212 | continue 213 | } 214 | 215 | port, _ := strconv.Atoi(p.Port()) 216 | 217 | if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { 218 | if destAddr == nil { 219 | destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port} 220 | } 221 | outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) 222 | if err != nil { 223 | s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) 224 | continue 225 | } 226 | s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) 227 | go func() { 228 | defer func() { 229 | if e := recover(); e != nil { 230 | s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack())) 231 | } 232 | }() 233 | defer s.udpRelatedPacketConns.Remove(srcAddr.String()) 234 | //out->local io copy 235 | buf := utils2.LeakyBuffer.Get() 236 | defer utils2.LeakyBuffer.Put(buf) 237 | for { 238 | outUDPConn.SetReadDeadline(time.Now().Add(time.Second * 5)) 239 | n, err := outUDPConn.Read(buf) 240 | outUDPConn.SetReadDeadline(time.Time{}) 241 | if err != nil { 242 | s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr) 243 | if isClosedErr(err) { 244 | return 245 | } 246 | continue 247 | } 248 | //var dlen = n 249 | //forward to local 250 | var v []byte 251 | //convert parent data to raw 252 | if len(s.udpParentKey) > 0 { 253 | v, err = goaes2.Decrypt(s.udpParentKey, buf[:n]) 254 | if err != nil { 255 | s.log.Printf("udp outconn parse packet fail, %s", err.Error()) 256 | continue 257 | } 258 | } else { 259 | v = buf[:n] 260 | } 261 | //now v is raw, try convert v to local 262 | if len(s.udpLocalKey) > 0 { 263 | v, _ = goaes2.Encrypt(s.udpLocalKey, v) 264 | } 265 | _, err = udpListener.WriteTo(v, srcAddr) 266 | // _, err = udpListener.WriteTo(buf[:n], srcAddr) 267 | 268 | if err != nil { 269 | s.udpRelatedPacketConns.Remove(srcAddr.String()) 270 | s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) 271 | if isClosedErr(err) { 272 | return 273 | } 274 | continue 275 | } else { 276 | //s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr) 277 | } 278 | } 279 | }() 280 | } else { 281 | outUDPConn = v.(*net.UDPConn) 282 | } 283 | //local->out io copy 284 | //forward to parent 285 | //p is raw, now convert it to parent 286 | var v []byte 287 | if len(s.udpParentKey) > 0 { 288 | v, _ = goaes2.Encrypt(s.udpParentKey, p.Bytes()) 289 | } else { 290 | v = p.Bytes() 291 | } 292 | _, err = outUDPConn.Write(v) 293 | // _, err = outUDPConn.Write(p.Bytes()) 294 | if err != nil { 295 | if isClosedErr(err) { 296 | return 297 | } 298 | s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr) 299 | continue 300 | } else { 301 | //s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr) 302 | } 303 | } 304 | 305 | } 306 | -------------------------------------------------------------------------------- /lib/goproxy/services/tcp/tcp.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | services2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services" 7 | kcpcfg2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services/kcpcfg" 8 | utils2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils" 9 | "io" 10 | logger "log" 11 | "net" 12 | "runtime/debug" 13 | "time" 14 | 15 | "strconv" 16 | ) 17 | 18 | type TCPArgs struct { 19 | Parent *string 20 | CertFile *string 21 | KeyFile *string 22 | CertBytes []byte 23 | KeyBytes []byte 24 | Local *string 25 | ParentType *string 26 | LocalType *string 27 | Timeout *int 28 | CheckParentInterval *int 29 | KCP kcpcfg2.KCPConfigArgs 30 | } 31 | 32 | type TCP struct { 33 | outPool utils2.OutConn 34 | cfg TCPArgs 35 | sc *utils2.ServerChannel 36 | isStop bool 37 | userConns utils2.ConcurrentMap 38 | log *logger.Logger 39 | } 40 | 41 | func NewTCP() services2.Service { 42 | return &TCP{ 43 | outPool: utils2.OutConn{}, 44 | cfg: TCPArgs{}, 45 | isStop: false, 46 | userConns: utils2.NewConcurrentMap(), 47 | } 48 | } 49 | func (s *TCP) CheckArgs() (err error) { 50 | if *s.cfg.Parent == "" { 51 | err = fmt.Errorf("parent required for %s %s", *s.cfg.LocalType, *s.cfg.Local) 52 | return 53 | } 54 | if *s.cfg.ParentType == "" { 55 | err = fmt.Errorf("parent type unkown,use -T ") 56 | return 57 | } 58 | if *s.cfg.ParentType == "tls" || *s.cfg.LocalType == "tls" { 59 | s.cfg.CertBytes, s.cfg.KeyBytes, err = utils2.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) 60 | if err != nil { 61 | return 62 | } 63 | } 64 | return 65 | } 66 | func (s *TCP) InitService() (err error) { 67 | s.InitOutConnPool() 68 | return 69 | } 70 | func (s *TCP) StopService() { 71 | defer func() { 72 | e := recover() 73 | if e != nil { 74 | s.log.Printf("stop tcp service crashed,%s", e) 75 | } else { 76 | s.log.Printf("service tcp stopped") 77 | } 78 | }() 79 | s.isStop = true 80 | if s.sc.Listener != nil && *s.sc.Listener != nil { 81 | (*s.sc.Listener).Close() 82 | } 83 | if s.sc.UDPListener != nil { 84 | (*s.sc.UDPListener).Close() 85 | } 86 | for _, c := range s.userConns.Items() { 87 | (*c.(*net.Conn)).Close() 88 | } 89 | } 90 | func (s *TCP) Start(args interface{}, log *logger.Logger) (err error) { 91 | s.log = log 92 | s.cfg = args.(TCPArgs) 93 | if err = s.CheckArgs(); err != nil { 94 | return 95 | } 96 | if err = s.InitService(); err != nil { 97 | return 98 | } 99 | s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) 100 | host, port, _ := net.SplitHostPort(*s.cfg.Local) 101 | p, _ := strconv.Atoi(port) 102 | sc := utils2.NewServerChannel(host, p, s.log) 103 | 104 | if *s.cfg.LocalType == "tcp" { 105 | err = sc.ListenTCP(s.callback) 106 | } else if *s.cfg.LocalType == "tls" { 107 | err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.callback) 108 | } else if *s.cfg.LocalType == "kcp" { 109 | err = sc.ListenKCP(s.cfg.KCP, s.callback, s.log) 110 | } 111 | if err != nil { 112 | return 113 | } 114 | s.log.Printf("%s proxy on %s", *s.cfg.LocalType, (*sc.Listener).Addr()) 115 | s.sc = &sc 116 | return 117 | } 118 | 119 | func (s *TCP) Clean() { 120 | s.StopService() 121 | } 122 | func (s *TCP) callback(inConn net.Conn) { 123 | defer func() { 124 | if err := recover(); err != nil { 125 | s.log.Printf("%s conn handler crashed with err : %s \nstack: %s", *s.cfg.LocalType, err, string(debug.Stack())) 126 | } 127 | }() 128 | var err error 129 | switch *s.cfg.ParentType { 130 | case "kcp": 131 | fallthrough 132 | case "tcp": 133 | fallthrough 134 | case "tls": 135 | err = s.OutToTCP(&inConn) 136 | case "udp": 137 | err = s.OutToUDP(&inConn) 138 | default: 139 | err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType) 140 | } 141 | if err != nil { 142 | s.log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err) 143 | utils2.CloseConn(&inConn) 144 | } 145 | } 146 | func (s *TCP) OutToTCP(inConn *net.Conn) (err error) { 147 | var outConn net.Conn 148 | outConn, err = s.outPool.Get() 149 | if err != nil { 150 | s.log.Printf("connect to %s , err:%s", *s.cfg.Parent, err) 151 | utils2.CloseConn(inConn) 152 | return 153 | } 154 | inAddr := (*inConn).RemoteAddr().String() 155 | //inLocalAddr := (*inConn).LocalAddr().String() 156 | outAddr := outConn.RemoteAddr().String() 157 | //outLocalAddr := outConn.LocalAddr().String() 158 | utils2.IoBind((*inConn), outConn, func(err interface{}) { 159 | s.log.Printf("conn %s - %s released", inAddr, outAddr) 160 | s.userConns.Remove(inAddr) 161 | }, s.log) 162 | s.log.Printf("conn %s - %s connected", inAddr, outAddr) 163 | if c, ok := s.userConns.Get(inAddr); ok { 164 | (*c.(*net.Conn)).Close() 165 | } 166 | s.userConns.Set(inAddr, inConn) 167 | return 168 | } 169 | func (s *TCP) OutToUDP(inConn *net.Conn) (err error) { 170 | s.log.Printf("conn created , remote : %s ", (*inConn).RemoteAddr()) 171 | for { 172 | if s.isStop { 173 | (*inConn).Close() 174 | return 175 | } 176 | srcAddr, body, err := utils2.ReadUDPPacket(bufio.NewReader(*inConn)) 177 | if err == io.EOF || err == io.ErrUnexpectedEOF { 178 | //s.log.Printf("connection %s released", srcAddr) 179 | utils2.CloseConn(inConn) 180 | break 181 | } 182 | //log.Debugf("udp packet revecived:%s,%v", srcAddr, body) 183 | dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent) 184 | if err != nil { 185 | s.log.Printf("can't resolve address: %s", err) 186 | utils2.CloseConn(inConn) 187 | break 188 | } 189 | clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} 190 | conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) 191 | if err != nil { 192 | s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) 193 | continue 194 | } 195 | conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 196 | _, err = conn.Write(body) 197 | if err != nil { 198 | s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) 199 | continue 200 | } 201 | //log.Debugf("send udp packet to %s success", dstAddr.String()) 202 | buf := make([]byte, 512) 203 | len, _, err := conn.ReadFromUDP(buf) 204 | if err != nil { 205 | s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) 206 | continue 207 | } 208 | respBody := buf[0:len] 209 | //log.Debugf("revecived udp packet from %s , %v", dstAddr.String(), respBody) 210 | _, err = (*inConn).Write(utils2.UDPPacket(srcAddr, respBody)) 211 | if err != nil { 212 | s.log.Printf("send udp response fail ,ERR:%s", err) 213 | utils2.CloseConn(inConn) 214 | break 215 | } 216 | //s.log.Printf("send udp response success ,from:%s", dstAddr.String()) 217 | } 218 | return 219 | 220 | } 221 | func (s *TCP) InitOutConnPool() { 222 | if *s.cfg.ParentType == "tls" || *s.cfg.ParentType == "tcp" || *s.cfg.ParentType == "kcp" { 223 | //dur int, isTLS bool, certBytes, keyBytes []byte, 224 | //parent string, timeout int, InitialCap int, MaxCap int 225 | s.outPool = utils2.NewOutConn( 226 | *s.cfg.CheckParentInterval, 227 | *s.cfg.ParentType, 228 | s.cfg.KCP, 229 | s.cfg.CertBytes, s.cfg.KeyBytes, nil, 230 | *s.cfg.Parent, 231 | *s.cfg.Timeout, 232 | ) 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /lib/goproxy/services/tunnel/tunnel_bridge.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | services2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services" 7 | utils2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils" 8 | logger "log" 9 | "net" 10 | "os" 11 | "strconv" 12 | "time" 13 | 14 | //"github.com/xtaci/smux" 15 | smux "github.com/hashicorp/yamux" 16 | ) 17 | 18 | const ( 19 | CONN_CLIENT_CONTROL = uint8(1) 20 | CONN_SERVER = uint8(4) 21 | CONN_CLIENT = uint8(5) 22 | ) 23 | 24 | type TunnelBridgeArgs struct { 25 | Parent *string 26 | CertFile *string 27 | KeyFile *string 28 | CertBytes []byte 29 | KeyBytes []byte 30 | Local *string 31 | Timeout *int 32 | Mux *bool 33 | } 34 | type ServerConn struct { 35 | //ClientLocalAddr string //tcp:2.2.22:333@ID 36 | Conn *net.Conn 37 | } 38 | type TunnelBridge struct { 39 | cfg TunnelBridgeArgs 40 | serverConns utils2.ConcurrentMap 41 | clientControlConns utils2.ConcurrentMap 42 | isStop bool 43 | log *logger.Logger 44 | } 45 | 46 | func NewTunnelBridge() services2.Service { 47 | return &TunnelBridge{ 48 | cfg: TunnelBridgeArgs{}, 49 | serverConns: utils2.NewConcurrentMap(), 50 | clientControlConns: utils2.NewConcurrentMap(), 51 | isStop: false, 52 | } 53 | } 54 | 55 | func (s *TunnelBridge) InitService() (err error) { 56 | return 57 | } 58 | func (s *TunnelBridge) CheckArgs() (err error) { 59 | if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { 60 | err = fmt.Errorf("cert and key file required") 61 | return 62 | } 63 | s.cfg.CertBytes, s.cfg.KeyBytes, err = utils2.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) 64 | return 65 | } 66 | func (s *TunnelBridge) StopService() { 67 | defer func() { 68 | e := recover() 69 | if e != nil { 70 | s.log.Printf("stop tbridge service crashed,%s", e) 71 | } else { 72 | s.log.Printf("service tbridge stopped") 73 | } 74 | }() 75 | s.isStop = true 76 | for _, sess := range s.clientControlConns.Items() { 77 | (*sess.(*net.Conn)).Close() 78 | } 79 | for _, sess := range s.serverConns.Items() { 80 | (*sess.(ServerConn).Conn).Close() 81 | } 82 | } 83 | func (s *TunnelBridge) Start(args interface{}, log *logger.Logger) (err error) { 84 | s.log = log 85 | s.cfg = args.(TunnelBridgeArgs) 86 | if err = s.CheckArgs(); err != nil { 87 | return 88 | } 89 | if err = s.InitService(); err != nil { 90 | return 91 | } 92 | host, port, _ := net.SplitHostPort(*s.cfg.Local) 93 | p, _ := strconv.Atoi(port) 94 | sc := utils2.NewServerChannel(host, p, s.log) 95 | 96 | err = sc.ListenTls(s.cfg.CertBytes, s.cfg.KeyBytes, nil, s.callback) 97 | if err != nil { 98 | return 99 | } 100 | s.log.Printf("proxy on tunnel bridge mode %s", (*sc.Listener).Addr()) 101 | return 102 | } 103 | func (s *TunnelBridge) Clean() { 104 | s.StopService() 105 | } 106 | func (s *TunnelBridge) callback(inConn net.Conn) { 107 | var err error 108 | //s.log.Printf("connection from %s ", inConn.RemoteAddr()) 109 | sess, err := smux.Server(inConn, &smux.Config{ 110 | AcceptBacklog: 256, 111 | EnableKeepAlive: true, 112 | KeepAliveInterval: 9 * time.Second, 113 | ConnectionWriteTimeout: 3 * time.Second, 114 | MaxStreamWindowSize: 512 * 1024, 115 | LogOutput: os.Stderr, 116 | }) 117 | if err != nil { 118 | s.log.Printf("new mux server conn error,ERR:%s", err) 119 | return 120 | } 121 | inConn, err = sess.AcceptStream() 122 | if err != nil { 123 | s.log.Printf("mux server conn accept error,ERR:%s", err) 124 | return 125 | } 126 | 127 | var buf = make([]byte, 1024) 128 | n, _ := inConn.Read(buf) 129 | reader := bytes.NewReader(buf[:n]) 130 | //reader := bufio.NewReader(inConn) 131 | 132 | var connType uint8 133 | err = utils2.ReadPacket(reader, &connType) 134 | if err != nil { 135 | s.log.Printf("read error,ERR:%s", err) 136 | return 137 | } 138 | switch connType { 139 | case CONN_SERVER: 140 | var key, ID, clientLocalAddr, serverID string 141 | err = utils2.ReadPacketData(reader, &key, &ID, &clientLocalAddr, &serverID) 142 | if err != nil { 143 | s.log.Printf("read error,ERR:%s", err) 144 | return 145 | } 146 | packet := utils2.BuildPacketData(ID, clientLocalAddr, serverID) 147 | s.log.Printf("server connection, key: %s , id: %s %s %s", key, ID, clientLocalAddr, serverID) 148 | 149 | //addr := clientLocalAddr + "@" + ID 150 | s.serverConns.Set(ID, ServerConn{ 151 | Conn: &inConn, 152 | }) 153 | for { 154 | if s.isStop { 155 | return 156 | } 157 | item, ok := s.clientControlConns.Get(key) 158 | if !ok { 159 | s.log.Printf("client %s control conn not exists", key) 160 | time.Sleep(time.Second * 3) 161 | continue 162 | } 163 | (*item.(*net.Conn)).SetWriteDeadline(time.Now().Add(time.Second * 3)) 164 | _, err := (*item.(*net.Conn)).Write(packet) 165 | (*item.(*net.Conn)).SetWriteDeadline(time.Time{}) 166 | if err != nil { 167 | s.log.Printf("%s client control conn write signal fail, err: %s, retrying...", key, err) 168 | time.Sleep(time.Second * 3) 169 | continue 170 | } else { 171 | // s.cmServer.Add(serverID, ID, &inConn) 172 | break 173 | } 174 | } 175 | case CONN_CLIENT: 176 | var key, ID, serverID string 177 | err = utils2.ReadPacketData(reader, &key, &ID, &serverID) 178 | if err != nil { 179 | s.log.Printf("read error,ERR:%s", err) 180 | return 181 | } 182 | s.log.Printf("client connection , key: %s , id: %s, server id:%s", key, ID, serverID) 183 | 184 | serverConnItem, ok := s.serverConns.Get(ID) 185 | if !ok { 186 | inConn.Close() 187 | s.log.Printf("server conn %s exists", ID) 188 | return 189 | } 190 | serverConn := serverConnItem.(ServerConn).Conn 191 | utils2.IoBind(*serverConn, inConn, func(err interface{}) { 192 | s.serverConns.Remove(ID) 193 | // s.cmClient.RemoveOne(key, ID) 194 | // s.cmServer.RemoveOne(serverID, ID) 195 | s.log.Printf("conn %s released", ID) 196 | }, s.log) 197 | // s.cmClient.Add(key, ID, &inConn) 198 | s.log.Printf("conn %s created", ID) 199 | 200 | case CONN_CLIENT_CONTROL: 201 | var key string 202 | err = utils2.ReadPacketData(reader, &key) 203 | if err != nil { 204 | s.log.Printf("read error,ERR:%s", err) 205 | return 206 | } 207 | s.log.Printf("client control connection, key: %s", key) 208 | if s.clientControlConns.Has(key) { 209 | item, _ := s.clientControlConns.Get(key) 210 | (*item.(*net.Conn)).Close() 211 | } 212 | s.clientControlConns.Set(key, &inConn) 213 | s.log.Printf("set client %s control conn", key) 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /lib/goproxy/services/tunnel/tunnel_client.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "crypto/tls" 5 | "fmt" 6 | services2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services" 7 | utils2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils" 8 | "io" 9 | logger "log" 10 | "net" 11 | "os" 12 | "time" 13 | 14 | //"github.com/xtaci/smux" 15 | smux "github.com/hashicorp/yamux" 16 | ) 17 | 18 | const ( 19 | CONN_SERVER_MUX = uint8(6) 20 | CONN_CLIENT_MUX = uint8(7) 21 | ) 22 | 23 | type TunnelClientArgs struct { 24 | Parent *string 25 | CertFile *string 26 | KeyFile *string 27 | CertBytes []byte 28 | KeyBytes []byte 29 | Key *string 30 | Timeout *int 31 | Mux *bool 32 | } 33 | type TunnelClient struct { 34 | cfg TunnelClientArgs 35 | ctrlConn net.Conn 36 | isStop bool 37 | userConns utils2.ConcurrentMap 38 | log *logger.Logger 39 | } 40 | 41 | func NewTunnelClient() services2.Service { 42 | return &TunnelClient{ 43 | cfg: TunnelClientArgs{}, 44 | userConns: utils2.NewConcurrentMap(), 45 | isStop: false, 46 | } 47 | } 48 | 49 | func (s *TunnelClient) InitService() (err error) { 50 | return 51 | } 52 | 53 | func (s *TunnelClient) CheckArgs() (err error) { 54 | if *s.cfg.Parent != "" { 55 | s.log.Printf("use tls parent %s", *s.cfg.Parent) 56 | } else { 57 | err = fmt.Errorf("parent required") 58 | return 59 | } 60 | if *s.cfg.CertFile == "" || *s.cfg.KeyFile == "" { 61 | err = fmt.Errorf("cert and key file required") 62 | return 63 | } 64 | s.cfg.CertBytes, s.cfg.KeyBytes, err = utils2.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) 65 | return 66 | } 67 | func (s *TunnelClient) StopService() { 68 | defer func() { 69 | e := recover() 70 | if e != nil { 71 | s.log.Printf("stop tclient service crashed,%s", e) 72 | } else { 73 | s.log.Printf("service tclient stopped") 74 | } 75 | }() 76 | s.isStop = true 77 | if s.ctrlConn != nil { 78 | s.ctrlConn.Close() 79 | } 80 | for _, c := range s.userConns.Items() { 81 | (*c.(*net.Conn)).Close() 82 | } 83 | } 84 | func (s *TunnelClient) Start(args interface{}, log *logger.Logger) (err error) { 85 | s.log = log 86 | s.cfg = args.(TunnelClientArgs) 87 | if err = s.CheckArgs(); err != nil { 88 | return 89 | } 90 | if err = s.InitService(); err != nil { 91 | return 92 | } 93 | s.log.Printf("proxy on tunnel client mode") 94 | 95 | for { 96 | if s.isStop { 97 | return 98 | } 99 | if s.ctrlConn != nil { 100 | s.ctrlConn.Close() 101 | } 102 | 103 | s.ctrlConn, err = s.GetInConn(CONN_CLIENT_CONTROL, *s.cfg.Key) 104 | if err != nil { 105 | s.log.Printf("control connection err: %s, retrying...", err) 106 | time.Sleep(time.Second * 3) 107 | if s.ctrlConn != nil { 108 | s.ctrlConn.Close() 109 | } 110 | continue 111 | } 112 | for { 113 | if s.isStop { 114 | return 115 | } 116 | var ID, clientLocalAddr, serverID string 117 | err = utils2.ReadPacketData(s.ctrlConn, &ID, &clientLocalAddr, &serverID) 118 | if err != nil { 119 | if s.ctrlConn != nil { 120 | s.ctrlConn.Close() 121 | } 122 | s.log.Printf("read connection signal err: %s, retrying...", err) 123 | break 124 | } 125 | s.log.Printf("signal revecived:%s %s %s", serverID, ID, clientLocalAddr) 126 | protocol := clientLocalAddr[:3] 127 | localAddr := clientLocalAddr[4:] 128 | if protocol == "udp" { 129 | go s.ServeUDP(localAddr, ID, serverID) 130 | } else { 131 | go s.ServeConn(localAddr, ID, serverID) 132 | } 133 | } 134 | } 135 | } 136 | func (s *TunnelClient) Clean() { 137 | s.StopService() 138 | } 139 | func (s *TunnelClient) GetInConn(typ uint8, data ...string) (outConn net.Conn, err error) { 140 | outConn, err = s.GetConn() 141 | if err != nil { 142 | err = fmt.Errorf("connection err: %s", err) 143 | return 144 | } 145 | _, err = outConn.Write(utils2.BuildPacket(typ, data...)) 146 | if err != nil { 147 | err = fmt.Errorf("write connection data err: %s ,retrying...", err) 148 | utils2.CloseConn(&outConn) 149 | return 150 | } 151 | return 152 | } 153 | func (s *TunnelClient) GetConn() (conn net.Conn, err error) { 154 | var _conn tls.Conn 155 | _conn, err = utils2.TlsConnectHost(*s.cfg.Parent, *s.cfg.Timeout, s.cfg.CertBytes, s.cfg.KeyBytes, nil) 156 | if err == nil { 157 | conn = net.Conn(&_conn) 158 | c, e := smux.Client(conn, &smux.Config{ 159 | AcceptBacklog: 256, 160 | EnableKeepAlive: true, 161 | KeepAliveInterval: 9 * time.Second, 162 | ConnectionWriteTimeout: 3 * time.Second, 163 | MaxStreamWindowSize: 512 * 1024, 164 | LogOutput: os.Stderr, 165 | }) 166 | if e != nil { 167 | s.log.Printf("new mux client conn error,ERR:%s", e) 168 | err = e 169 | return 170 | } 171 | conn, e = c.OpenStream() 172 | if e != nil { 173 | s.log.Printf("mux client conn open stream error,ERR:%s", e) 174 | err = e 175 | return 176 | } 177 | } 178 | return 179 | } 180 | func (s *TunnelClient) ServeUDP(localAddr, ID, serverID string) { 181 | var inConn net.Conn 182 | var err error 183 | // for { 184 | for { 185 | if s.isStop { 186 | if inConn != nil { 187 | inConn.Close() 188 | } 189 | return 190 | } 191 | // s.cm.RemoveOne(*s.cfg.Key, ID) 192 | inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID) 193 | if err != nil { 194 | utils2.CloseConn(&inConn) 195 | s.log.Printf("connection err: %s, retrying...", err) 196 | time.Sleep(time.Second * 3) 197 | continue 198 | } else { 199 | break 200 | } 201 | } 202 | // s.cm.Add(*s.cfg.Key, ID, &inConn) 203 | s.log.Printf("conn %s created", ID) 204 | 205 | for { 206 | if s.isStop { 207 | return 208 | } 209 | srcAddr, body, err := utils2.ReadUDPPacket(inConn) 210 | if err == io.EOF || err == io.ErrUnexpectedEOF { 211 | s.log.Printf("connection %s released", ID) 212 | utils2.CloseConn(&inConn) 213 | break 214 | } else if err != nil { 215 | s.log.Printf("udp packet revecived fail, err: %s", err) 216 | } else { 217 | //s.log.Printf("udp packet revecived:%s,%v", srcAddr, body) 218 | go s.processUDPPacket(&inConn, srcAddr, localAddr, body) 219 | } 220 | 221 | } 222 | // } 223 | } 224 | func (s *TunnelClient) processUDPPacket(inConn *net.Conn, srcAddr, localAddr string, body []byte) { 225 | dstAddr, err := net.ResolveUDPAddr("udp", localAddr) 226 | if err != nil { 227 | s.log.Printf("can't resolve address: %s", err) 228 | utils2.CloseConn(inConn) 229 | return 230 | } 231 | clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} 232 | conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) 233 | if err != nil { 234 | s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) 235 | return 236 | } 237 | conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 238 | _, err = conn.Write(body) 239 | if err != nil { 240 | s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) 241 | return 242 | } 243 | //s.log.Printf("send udp packet to %s success", dstAddr.String()) 244 | buf := make([]byte, 1024) 245 | length, _, err := conn.ReadFromUDP(buf) 246 | if err != nil { 247 | s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) 248 | return 249 | } 250 | respBody := buf[0:length] 251 | //s.log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody) 252 | bs := utils2.UDPPacket(srcAddr, respBody) 253 | _, err = (*inConn).Write(bs) 254 | if err != nil { 255 | s.log.Printf("send udp response fail ,ERR:%s", err) 256 | utils2.CloseConn(inConn) 257 | return 258 | } 259 | //s.log.Printf("send udp response success ,from:%s ,%d ,%v", dstAddr.String(), len(bs), bs) 260 | } 261 | func (s *TunnelClient) ServeConn(localAddr, ID, serverID string) { 262 | var inConn, outConn net.Conn 263 | var err error 264 | for { 265 | if s.isStop { 266 | return 267 | } 268 | inConn, err = s.GetInConn(CONN_CLIENT, *s.cfg.Key, ID, serverID) 269 | if err != nil { 270 | utils2.CloseConn(&inConn) 271 | s.log.Printf("connection err: %s, retrying...", err) 272 | time.Sleep(time.Second * 3) 273 | continue 274 | } else { 275 | break 276 | } 277 | } 278 | 279 | i := 0 280 | for { 281 | if s.isStop { 282 | return 283 | } 284 | i++ 285 | outConn, err = utils2.ConnectHost(localAddr, *s.cfg.Timeout) 286 | if err == nil || i == 3 { 287 | break 288 | } else { 289 | if i == 3 { 290 | s.log.Printf("connect to %s err: %s, retrying...", localAddr, err) 291 | time.Sleep(2 * time.Second) 292 | continue 293 | } 294 | } 295 | } 296 | if err != nil { 297 | utils2.CloseConn(&inConn) 298 | utils2.CloseConn(&outConn) 299 | s.log.Printf("build connection error, err: %s", err) 300 | return 301 | } 302 | inAddr := inConn.RemoteAddr().String() 303 | utils2.IoBind(inConn, outConn, func(err interface{}) { 304 | s.log.Printf("conn %s released", ID) 305 | s.userConns.Remove(inAddr) 306 | }, s.log) 307 | if c, ok := s.userConns.Get(inAddr); ok { 308 | (*c.(*net.Conn)).Close() 309 | } 310 | s.userConns.Set(inAddr, &inConn) 311 | s.log.Printf("conn %s created", ID) 312 | } 313 | -------------------------------------------------------------------------------- /lib/goproxy/services/udp/udp.go: -------------------------------------------------------------------------------- 1 | package udp 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | services2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services" 7 | kcpcfg2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services/kcpcfg" 8 | utils2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils" 9 | "hash/crc32" 10 | "io" 11 | logger "log" 12 | "net" 13 | "runtime/debug" 14 | "strconv" 15 | "strings" 16 | "time" 17 | ) 18 | 19 | type UDPArgs struct { 20 | Parent *string 21 | CertFile *string 22 | KeyFile *string 23 | CertBytes []byte 24 | KeyBytes []byte 25 | Local *string 26 | ParentType *string 27 | Timeout *int 28 | CheckParentInterval *int 29 | } 30 | type UDP struct { 31 | p utils2.ConcurrentMap 32 | outPool utils2.OutConn 33 | cfg UDPArgs 34 | sc *utils2.ServerChannel 35 | isStop bool 36 | log *logger.Logger 37 | } 38 | 39 | func NewUDP() services2.Service { 40 | return &UDP{ 41 | outPool: utils2.OutConn{}, 42 | p: utils2.NewConcurrentMap(), 43 | isStop: false, 44 | } 45 | } 46 | func (s *UDP) CheckArgs() (err error) { 47 | if *s.cfg.Parent == "" { 48 | err = fmt.Errorf("parent required for udp %s", *s.cfg.Local) 49 | return 50 | } 51 | if *s.cfg.ParentType == "" { 52 | err = fmt.Errorf("parent type unkown,use -T ") 53 | return 54 | } 55 | if *s.cfg.ParentType == "tls" { 56 | s.cfg.CertBytes, s.cfg.KeyBytes, err = utils2.TlsBytes(*s.cfg.CertFile, *s.cfg.KeyFile) 57 | if err != nil { 58 | return 59 | } 60 | } 61 | return 62 | } 63 | func (s *UDP) InitService() (err error) { 64 | if *s.cfg.ParentType != "udp" { 65 | s.InitOutConnPool() 66 | } 67 | return 68 | } 69 | func (s *UDP) StopService() { 70 | defer func() { 71 | e := recover() 72 | if e != nil { 73 | s.log.Printf("stop udp service crashed,%s", e) 74 | } else { 75 | s.log.Printf("service udp stopped") 76 | } 77 | }() 78 | s.isStop = true 79 | if s.sc.Listener != nil && *s.sc.Listener != nil { 80 | (*s.sc.Listener).Close() 81 | } 82 | if s.sc.UDPListener != nil { 83 | (*s.sc.UDPListener).Close() 84 | } 85 | } 86 | func (s *UDP) Start(args interface{}, log *logger.Logger) (err error) { 87 | s.log = log 88 | s.cfg = args.(UDPArgs) 89 | if err = s.CheckArgs(); err != nil { 90 | return 91 | } 92 | s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) 93 | if err = s.InitService(); err != nil { 94 | return 95 | } 96 | host, port, _ := net.SplitHostPort(*s.cfg.Local) 97 | p, _ := strconv.Atoi(port) 98 | sc := utils2.NewServerChannel(host, p, s.log) 99 | s.sc = &sc 100 | err = sc.ListenUDP(s.callback) 101 | if err != nil { 102 | return 103 | } 104 | s.log.Printf("udp proxy on %s", (*sc.UDPListener).LocalAddr()) 105 | return 106 | } 107 | 108 | func (s *UDP) Clean() { 109 | s.StopService() 110 | } 111 | func (s *UDP) callback(packet []byte, localAddr, srcAddr *net.UDPAddr) { 112 | defer func() { 113 | if err := recover(); err != nil { 114 | s.log.Printf("udp conn handler crashed with err : %s \nstack: %s", err, string(debug.Stack())) 115 | } 116 | }() 117 | var err error 118 | switch *s.cfg.ParentType { 119 | case "tcp": 120 | fallthrough 121 | case "tls": 122 | err = s.OutToTCP(packet, localAddr, srcAddr) 123 | case "udp": 124 | err = s.OutToUDP(packet, localAddr, srcAddr) 125 | default: 126 | err = fmt.Errorf("unkown parent type %s", *s.cfg.ParentType) 127 | } 128 | if err != nil { 129 | s.log.Printf("connect to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err) 130 | } 131 | } 132 | func (s *UDP) GetConn(connKey string) (conn net.Conn, isNew bool, err error) { 133 | isNew = !s.p.Has(connKey) 134 | var _conn interface{} 135 | if isNew { 136 | _conn, err = s.outPool.Get() 137 | if err != nil { 138 | return nil, false, err 139 | } 140 | s.p.Set(connKey, _conn) 141 | } else { 142 | _conn, _ = s.p.Get(connKey) 143 | } 144 | conn = _conn.(net.Conn) 145 | return 146 | } 147 | func (s *UDP) OutToTCP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) { 148 | numLocal := crc32.ChecksumIEEE([]byte(localAddr.String())) 149 | numSrc := crc32.ChecksumIEEE([]byte(srcAddr.String())) 150 | mod := uint32(10) 151 | if mod == 0 { 152 | mod = 10 153 | } 154 | connKey := uint64((numLocal/10)*10 + numSrc%mod) 155 | conn, isNew, err := s.GetConn(fmt.Sprintf("%d", connKey)) 156 | if err != nil { 157 | s.log.Printf("upd get conn to %s parent %s fail, ERR:%s", *s.cfg.ParentType, *s.cfg.Parent, err) 158 | return 159 | } 160 | if isNew { 161 | go func() { 162 | defer func() { 163 | if err := recover(); err != nil { 164 | s.log.Printf("udp conn handler out to tcp crashed with err : %s \nstack: %s", err, string(debug.Stack())) 165 | } 166 | }() 167 | s.log.Printf("conn %d created , local: %s", connKey, srcAddr.String()) 168 | for { 169 | if s.isStop { 170 | conn.Close() 171 | return 172 | } 173 | srcAddrFromConn, body, err := utils2.ReadUDPPacket(bufio.NewReader(conn)) 174 | if err == io.EOF || err == io.ErrUnexpectedEOF { 175 | //s.log.Printf("connection %d released", connKey) 176 | s.p.Remove(fmt.Sprintf("%d", connKey)) 177 | break 178 | } 179 | if err != nil { 180 | s.log.Printf("parse revecived udp packet fail, err: %s", err) 181 | continue 182 | } 183 | //s.log.Printf("udp packet revecived over parent , local:%s", srcAddrFromConn) 184 | _srcAddr := strings.Split(srcAddrFromConn, ":") 185 | if len(_srcAddr) != 2 { 186 | s.log.Printf("parse revecived udp packet fail, addr error : %s", srcAddrFromConn) 187 | continue 188 | } 189 | port, _ := strconv.Atoi(_srcAddr[1]) 190 | dstAddr := &net.UDPAddr{IP: net.ParseIP(_srcAddr[0]), Port: port} 191 | _, err = s.sc.UDPListener.WriteToUDP(body, dstAddr) 192 | if err != nil { 193 | s.log.Printf("udp response to local %s fail,ERR:%s", srcAddr, err) 194 | continue 195 | } 196 | //s.log.Printf("udp response to local %s success", srcAddr) 197 | } 198 | }() 199 | } 200 | //s.log.Printf("select conn %d , local: %s", connKey, srcAddr.String()) 201 | writer := bufio.NewWriter(conn) 202 | //fmt.Println(conn, writer) 203 | writer.Write(utils2.UDPPacket(srcAddr.String(), packet)) 204 | err = writer.Flush() 205 | if err != nil { 206 | s.log.Printf("write udp packet to %s fail ,flush err:%s", *s.cfg.Parent, err) 207 | return 208 | } 209 | //s.log.Printf("write packet %v", packet) 210 | return 211 | } 212 | func (s *UDP) OutToUDP(packet []byte, localAddr, srcAddr *net.UDPAddr) (err error) { 213 | //s.log.Printf("udp packet revecived:%s,%v", srcAddr, packet) 214 | dstAddr, err := net.ResolveUDPAddr("udp", *s.cfg.Parent) 215 | if err != nil { 216 | s.log.Printf("resolve udp addr %s fail fail,ERR:%s", dstAddr.String(), err) 217 | return 218 | } 219 | clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} 220 | conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) 221 | if err != nil { 222 | s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) 223 | return 224 | } 225 | conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) 226 | _, err = conn.Write(packet) 227 | if err != nil { 228 | s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) 229 | return 230 | } 231 | //s.log.Printf("send udp packet to %s success", dstAddr.String()) 232 | buf := make([]byte, 512) 233 | len, _, err := conn.ReadFromUDP(buf) 234 | if err != nil { 235 | s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) 236 | return 237 | } 238 | //s.log.Printf("revecived udp packet from %s , %v", dstAddr.String(), respBody) 239 | _, err = s.sc.UDPListener.WriteToUDP(buf[0:len], srcAddr) 240 | if err != nil { 241 | s.log.Printf("send udp response to cluster fail ,ERR:%s", err) 242 | return 243 | } 244 | //s.log.Printf("send udp response to cluster success ,from:%s", dstAddr.String()) 245 | return 246 | } 247 | func (s *UDP) InitOutConnPool() { 248 | if *s.cfg.ParentType == "tls" || *s.cfg.ParentType == "tcp" { 249 | //dur int, isTLS bool, certBytes, keyBytes []byte, 250 | //parent string, timeout int, InitialCap int, MaxCap int 251 | s.outPool = utils2.NewOutConn( 252 | *s.cfg.CheckParentInterval, 253 | *s.cfg.ParentType, 254 | kcpcfg2.KCPConfigArgs{}, 255 | s.cfg.CertBytes, s.cfg.KeyBytes, nil, 256 | *s.cfg.Parent, 257 | *s.cfg.Timeout, 258 | ) 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /lib/goproxy/utils/aes/aes.go: -------------------------------------------------------------------------------- 1 | // Playbook - http://play.golang.org/p/3wFl4lacjX 2 | 3 | package goaes 4 | 5 | import ( 6 | "bytes" 7 | "crypto/aes" 8 | "crypto/cipher" 9 | "crypto/rand" 10 | "errors" 11 | "io" 12 | "strings" 13 | ) 14 | 15 | func addBase64Padding(value string) string { 16 | m := len(value) % 4 17 | if m != 0 { 18 | value += strings.Repeat("=", 4-m) 19 | } 20 | 21 | return value 22 | } 23 | 24 | func removeBase64Padding(value string) string { 25 | return strings.Replace(value, "=", "", -1) 26 | } 27 | 28 | func Pad(src []byte) []byte { 29 | padding := aes.BlockSize - len(src)%aes.BlockSize 30 | padtext := bytes.Repeat([]byte{byte(padding)}, padding) 31 | return append(src, padtext...) 32 | } 33 | 34 | func Unpad(src []byte) ([]byte, error) { 35 | length := len(src) 36 | unpadding := int(src[length-1]) 37 | 38 | if unpadding > length { 39 | return nil, errors.New("unpad error. This could happen when incorrect encryption key is used") 40 | } 41 | 42 | return src[:(length - unpadding)], nil 43 | } 44 | 45 | func Encrypt(key []byte, text []byte) ([]byte, error) { 46 | block, err := aes.NewCipher(key) 47 | if err != nil { 48 | return nil, err 49 | } 50 | 51 | msg := Pad(text) 52 | ciphertext := make([]byte, aes.BlockSize+len(msg)) 53 | iv := ciphertext[:aes.BlockSize] 54 | if _, err := io.ReadFull(rand.Reader, iv); err != nil { 55 | return nil, err 56 | } 57 | 58 | cfb := cipher.NewCFBEncrypter(block, iv) 59 | cfb.XORKeyStream(ciphertext[aes.BlockSize:], []byte(msg)) 60 | 61 | return ciphertext, nil 62 | } 63 | 64 | func Decrypt(key []byte, text []byte) ([]byte, error) { 65 | block, err := aes.NewCipher(key) 66 | if err != nil { 67 | return nil, err 68 | } 69 | if (len(text) % aes.BlockSize) != 0 { 70 | return nil, errors.New("blocksize must be multipe of decoded message length") 71 | } 72 | iv := text[:aes.BlockSize] 73 | msg := text[aes.BlockSize:] 74 | 75 | cfb := cipher.NewCFBDecrypter(block, iv) 76 | cfb.XORKeyStream(msg, msg) 77 | 78 | unpadMsg, err := Unpad(msg) 79 | if err != nil { 80 | return nil, err 81 | } 82 | 83 | return unpadMsg, nil 84 | } 85 | -------------------------------------------------------------------------------- /lib/goproxy/utils/conncrypt/conncrypt.go: -------------------------------------------------------------------------------- 1 | package conncrypt 2 | 3 | import ( 4 | "crypto/aes" 5 | "crypto/cipher" 6 | "crypto/sha256" 7 | "hash" 8 | "io" 9 | "net" 10 | 11 | "golang.org/x/crypto/pbkdf2" 12 | ) 13 | 14 | //Confg defaults 15 | const DefaultIterations = 1024 16 | const DefaultKeySize = 24 //256bits 17 | var DefaultHashFunc = sha256.New 18 | var DefaultSalt = []byte(` 19 | (;QUHj.BQ?RXzYSO]ifkXp/G!kFmWyXyEV6Nt!d|@bo+N$L9+SOd,6acYKY_ec+(x"R";\'4&fTAVu92GVA-wxBptOTM^2,iP5%)wnhW 21 | hwk=]Snsgymt!3gbP2pe=J//}1a?lp9ej=&TB!C_V(cT2?z8wyoL_-13fd[] 22 | `) //salt must be predefined in order to derive the same key 23 | 24 | //Config stores the PBKDF2 key generation parameters 25 | type Config struct { 26 | Password string 27 | Salt []byte 28 | Iterations int 29 | KeySize int 30 | HashFunc func() hash.Hash 31 | } 32 | 33 | //New creates an AES encrypted net.Conn by generating 34 | //a key using PBKDF2 with the provided configuration 35 | func New(conn net.Conn, c *Config) net.Conn { 36 | //set defaults 37 | if len(c.Salt) == 0 { 38 | c.Salt = DefaultSalt 39 | } 40 | if c.Iterations == 0 { 41 | c.Iterations = DefaultIterations 42 | } 43 | if c.KeySize != 16 && c.KeySize != 24 && c.KeySize != 32 { 44 | c.KeySize = DefaultKeySize 45 | } 46 | if c.HashFunc == nil { 47 | c.HashFunc = DefaultHashFunc 48 | } 49 | 50 | //generate key 51 | key := pbkdf2.Key([]byte(c.Password), c.Salt, c.Iterations, c.KeySize, c.HashFunc) 52 | 53 | // could use scrypt, but it's a bit slow... 54 | // dk, err := scrypt.Key([]byte(c.Password), c.Salt, 16384, 8, 1, 32) 55 | 56 | //key will be always be the correct size so this will never error 57 | conn, _ = NewFromKey(conn, key) 58 | return conn 59 | } 60 | 61 | //NewFromKey creates an AES encrypted net.Conn using the provided key 62 | func NewFromKey(conn net.Conn, key []byte) (net.Conn, error) { 63 | block, err := aes.NewCipher([]byte(key)) 64 | if err != nil { 65 | return nil, err 66 | } 67 | //hash(key) -> read IV 68 | riv := DefaultHashFunc().Sum(key) 69 | rstream := cipher.NewCFBDecrypter(block, riv[:aes.BlockSize]) 70 | reader := &cipher.StreamReader{S: rstream, R: conn} 71 | //hash(read IV) -> write IV 72 | wiv := DefaultHashFunc().Sum(riv) 73 | wstream := cipher.NewCFBEncrypter(block, wiv[:aes.BlockSize]) 74 | writer := &cipher.StreamWriter{S: wstream, W: conn} 75 | 76 | return &cryptoConn{ 77 | Conn: conn, 78 | r: reader, 79 | w: writer, 80 | }, nil 81 | } 82 | 83 | type cryptoConn struct { 84 | net.Conn 85 | r io.Reader 86 | w io.Writer 87 | } 88 | 89 | //replace read and write methods 90 | func (c *cryptoConn) Read(p []byte) (int, error) { 91 | return c.r.Read(p) 92 | } 93 | func (c *cryptoConn) Write(p []byte) (int, error) { 94 | return c.w.Write(p) 95 | } 96 | -------------------------------------------------------------------------------- /lib/goproxy/utils/id/xid.go: -------------------------------------------------------------------------------- 1 | // Package xid is a globally unique id generator suited for web scale 2 | // 3 | // Xid is using Mongo Object ID algorithm to generate globally unique ids: 4 | // https://docs.mongodb.org/manual/reference/object-id/ 5 | // 6 | // - 4-byte value representing the seconds since the Unix epoch, 7 | // - 3-byte machine identifier, 8 | // - 2-byte process id, and 9 | // - 3-byte counter, starting with a random value. 10 | // 11 | // The binary representation of the id is compatible with Mongo 12 bytes Object IDs. 12 | // The string representation is using base32 hex (w/o padding) for better space efficiency 13 | // when stored in that form (20 bytes). The hex variant of base32 is used to retain the 14 | // sortable property of the id. 15 | // 16 | // Xid doesn't use base64 because case sensitivity and the 2 non alphanum chars may be an 17 | // issue when transported as a string between various systems. Base36 wasn't retained either 18 | // because 1/ it's not standard 2/ the resulting size is not predictable (not bit aligned) 19 | // and 3/ it would not remain sortable. To validate a base32 `xid`, expect a 20 chars long, 20 | // all lowercase sequence of `a` to `v` letters and `0` to `9` numbers (`[0-9a-v]{20}`). 21 | // 22 | // UUID is 16 bytes (128 bits), snowflake is 8 bytes (64 bits), xid stands in between 23 | // with 12 bytes with a more compact string representation ready for the web and no 24 | // required configuration or central generation server. 25 | // 26 | // Features: 27 | // 28 | // - Size: 12 bytes (96 bits), smaller than UUID, larger than snowflake 29 | // - Base32 hex encoded by default (16 bytes storage when transported as printable string) 30 | // - Non configured, you don't need set a unique machine and/or data center id 31 | // - K-ordered 32 | // - Embedded time with 1 second precision 33 | // - Unicity guaranted for 16,777,216 (24 bits) unique ids per second and per host/process 34 | // 35 | // Best used with xlog's RequestIDHandler (https://godoc.org/github.com/rs/xlog#RequestIDHandler). 36 | // 37 | // References: 38 | // 39 | // - http://www.slideshare.net/davegardnerisme/unique-id-generation-in-distributed-systems 40 | // - https://en.wikipedia.org/wiki/Universally_unique_identifier 41 | // - https://blog.twitter.com/2010/announcing-snowflake 42 | package xid 43 | 44 | import ( 45 | "crypto/md5" 46 | "crypto/rand" 47 | "database/sql/driver" 48 | "encoding/binary" 49 | "errors" 50 | "fmt" 51 | "os" 52 | "sync/atomic" 53 | "time" 54 | ) 55 | 56 | // Code inspired from mgo/bson ObjectId 57 | 58 | // ID represents a unique request id 59 | type ID [rawLen]byte 60 | 61 | const ( 62 | encodedLen = 20 // string encoded len 63 | decodedLen = 15 // len after base32 decoding with the padded data 64 | rawLen = 12 // binary raw len 65 | 66 | // encoding stores a custom version of the base32 encoding with lower case 67 | // letters. 68 | encoding = "0123456789abcdefghijklmnopqrstuv" 69 | ) 70 | 71 | // ErrInvalidID is returned when trying to unmarshal an invalid ID 72 | var ErrInvalidID = errors.New("xid: invalid ID") 73 | 74 | // objectIDCounter is atomically incremented when generating a new ObjectId 75 | // using NewObjectId() function. It's used as a counter part of an id. 76 | // This id is initialized with a random value. 77 | var objectIDCounter = randInt() 78 | 79 | // machineId stores machine id generated once and used in subsequent calls 80 | // to NewObjectId function. 81 | var machineID = readMachineID() 82 | 83 | // pid stores the current process id 84 | var pid = os.Getpid() 85 | 86 | // dec is the decoding map for base32 encoding 87 | var dec [256]byte 88 | 89 | func init() { 90 | for i := 0; i < len(dec); i++ { 91 | dec[i] = 0xFF 92 | } 93 | for i := 0; i < len(encoding); i++ { 94 | dec[encoding[i]] = byte(i) 95 | } 96 | } 97 | 98 | // readMachineId generates machine id and puts it into the machineId global 99 | // variable. If this function fails to get the hostname, it will cause 100 | // a runtime error. 101 | func readMachineID() []byte { 102 | id := make([]byte, 3) 103 | if hostname, err := os.Hostname(); err == nil { 104 | hw := md5.New() 105 | hw.Write([]byte(hostname)) 106 | copy(id, hw.Sum(nil)) 107 | } else { 108 | // Fallback to rand number if machine id can't be gathered 109 | if _, randErr := rand.Reader.Read(id); randErr != nil { 110 | panic(fmt.Errorf("xid: cannot get hostname nor generate a random number: %v; %v", err, randErr)) 111 | } 112 | } 113 | return id 114 | } 115 | 116 | // randInt generates a random uint32 117 | func randInt() uint32 { 118 | b := make([]byte, 3) 119 | if _, err := rand.Reader.Read(b); err != nil { 120 | panic(fmt.Errorf("xid: cannot generate random number: %v;", err)) 121 | } 122 | return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]) 123 | } 124 | 125 | // New generates a globaly unique ID 126 | func New() ID { 127 | var id ID 128 | // Timestamp, 4 bytes, big endian 129 | binary.BigEndian.PutUint32(id[:], uint32(time.Now().Unix())) 130 | // Machine, first 3 bytes of md5(hostname) 131 | id[4] = machineID[0] 132 | id[5] = machineID[1] 133 | id[6] = machineID[2] 134 | // Pid, 2 bytes, specs don't specify endianness, but we use big endian. 135 | id[7] = byte(pid >> 8) 136 | id[8] = byte(pid) 137 | // Increment, 3 bytes, big endian 138 | i := atomic.AddUint32(&objectIDCounter, 1) 139 | id[9] = byte(i >> 16) 140 | id[10] = byte(i >> 8) 141 | id[11] = byte(i) 142 | return id 143 | } 144 | 145 | // FromString reads an ID from its string representation 146 | func FromString(id string) (ID, error) { 147 | i := &ID{} 148 | err := i.UnmarshalText([]byte(id)) 149 | return *i, err 150 | } 151 | 152 | // String returns a base32 hex lowercased with no padding representation of the id (char set is 0-9, a-v). 153 | func (id ID) String() string { 154 | text := make([]byte, encodedLen) 155 | encode(text, id[:]) 156 | return string(text) 157 | } 158 | 159 | // MarshalText implements encoding/text TextMarshaler interface 160 | func (id ID) MarshalText() ([]byte, error) { 161 | text := make([]byte, encodedLen) 162 | encode(text, id[:]) 163 | return text, nil 164 | } 165 | 166 | // encode by unrolling the stdlib base32 algorithm + removing all safe checks 167 | func encode(dst, id []byte) { 168 | dst[0] = encoding[id[0]>>3] 169 | dst[1] = encoding[(id[1]>>6)&0x1F|(id[0]<<2)&0x1F] 170 | dst[2] = encoding[(id[1]>>1)&0x1F] 171 | dst[3] = encoding[(id[2]>>4)&0x1F|(id[1]<<4)&0x1F] 172 | dst[4] = encoding[id[3]>>7|(id[2]<<1)&0x1F] 173 | dst[5] = encoding[(id[3]>>2)&0x1F] 174 | dst[6] = encoding[id[4]>>5|(id[3]<<3)&0x1F] 175 | dst[7] = encoding[id[4]&0x1F] 176 | dst[8] = encoding[id[5]>>3] 177 | dst[9] = encoding[(id[6]>>6)&0x1F|(id[5]<<2)&0x1F] 178 | dst[10] = encoding[(id[6]>>1)&0x1F] 179 | dst[11] = encoding[(id[7]>>4)&0x1F|(id[6]<<4)&0x1F] 180 | dst[12] = encoding[id[8]>>7|(id[7]<<1)&0x1F] 181 | dst[13] = encoding[(id[8]>>2)&0x1F] 182 | dst[14] = encoding[(id[9]>>5)|(id[8]<<3)&0x1F] 183 | dst[15] = encoding[id[9]&0x1F] 184 | dst[16] = encoding[id[10]>>3] 185 | dst[17] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F] 186 | dst[18] = encoding[(id[11]>>1)&0x1F] 187 | dst[19] = encoding[(id[11]<<4)&0x1F] 188 | } 189 | 190 | // UnmarshalText implements encoding/text TextUnmarshaler interface 191 | func (id *ID) UnmarshalText(text []byte) error { 192 | if len(text) != encodedLen { 193 | return ErrInvalidID 194 | } 195 | for _, c := range text { 196 | if dec[c] == 0xFF { 197 | return ErrInvalidID 198 | } 199 | } 200 | decode(id, text) 201 | return nil 202 | } 203 | 204 | // decode by unrolling the stdlib base32 algorithm + removing all safe checks 205 | func decode(id *ID, src []byte) { 206 | id[0] = dec[src[0]]<<3 | dec[src[1]]>>2 207 | id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4 208 | id[2] = dec[src[3]]<<4 | dec[src[4]]>>1 209 | id[3] = dec[src[4]]<<7 | dec[src[5]]<<2 | dec[src[6]]>>3 210 | id[4] = dec[src[6]]<<5 | dec[src[7]] 211 | id[5] = dec[src[8]]<<3 | dec[src[9]]>>2 212 | id[6] = dec[src[9]]<<6 | dec[src[10]]<<1 | dec[src[11]]>>4 213 | id[7] = dec[src[11]]<<4 | dec[src[12]]>>1 214 | id[8] = dec[src[12]]<<7 | dec[src[13]]<<2 | dec[src[14]]>>3 215 | id[9] = dec[src[14]]<<5 | dec[src[15]] 216 | id[10] = dec[src[16]]<<3 | dec[src[17]]>>2 217 | id[11] = dec[src[17]]<<6 | dec[src[18]]<<1 | dec[src[19]]>>4 218 | } 219 | 220 | // Time returns the timestamp part of the id. 221 | // It's a runtime error to call this method with an invalid id. 222 | func (id ID) Time() time.Time { 223 | // First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch. 224 | secs := int64(binary.BigEndian.Uint32(id[0:4])) 225 | return time.Unix(secs, 0) 226 | } 227 | 228 | // Machine returns the 3-byte machine id part of the id. 229 | // It's a runtime error to call this method with an invalid id. 230 | func (id ID) Machine() []byte { 231 | return id[4:7] 232 | } 233 | 234 | // Pid returns the process id part of the id. 235 | // It's a runtime error to call this method with an invalid id. 236 | func (id ID) Pid() uint16 { 237 | return binary.BigEndian.Uint16(id[7:9]) 238 | } 239 | 240 | // Counter returns the incrementing value part of the id. 241 | // It's a runtime error to call this method with an invalid id. 242 | func (id ID) Counter() int32 { 243 | b := id[9:12] 244 | // Counter is stored as big-endian 3-byte value 245 | return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])) 246 | } 247 | 248 | // Value implements the driver.Valuer interface. 249 | func (id ID) Value() (driver.Value, error) { 250 | b, err := id.MarshalText() 251 | return string(b), err 252 | } 253 | 254 | // Scan implements the sql.Scanner interface. 255 | func (id *ID) Scan(value interface{}) (err error) { 256 | switch val := value.(type) { 257 | case string: 258 | return id.UnmarshalText([]byte(val)) 259 | case []byte: 260 | return id.UnmarshalText(val) 261 | default: 262 | return fmt.Errorf("xid: scanning unsupported type: %T", value) 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /lib/goproxy/utils/io-limiter.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "io" 6 | "time" 7 | 8 | "golang.org/x/time/rate" 9 | ) 10 | 11 | const burstLimit = 1000 * 1000 * 1000 12 | 13 | type Reader struct { 14 | r io.Reader 15 | limiter *rate.Limiter 16 | ctx context.Context 17 | } 18 | 19 | type Writer struct { 20 | w io.Writer 21 | limiter *rate.Limiter 22 | ctx context.Context 23 | } 24 | 25 | // NewReader returns a reader that implements io.Reader with rate limiting. 26 | func NewReader(r io.Reader) *Reader { 27 | return &Reader{ 28 | r: r, 29 | ctx: context.Background(), 30 | } 31 | } 32 | 33 | // NewReaderWithContext returns a reader that implements io.Reader with rate limiting. 34 | func NewReaderWithContext(r io.Reader, ctx context.Context) *Reader { 35 | return &Reader{ 36 | r: r, 37 | ctx: ctx, 38 | } 39 | } 40 | 41 | // NewWriter returns a writer that implements io.Writer with rate limiting. 42 | func NewWriter(w io.Writer) *Writer { 43 | return &Writer{ 44 | w: w, 45 | ctx: context.Background(), 46 | } 47 | } 48 | 49 | // NewWriterWithContext returns a writer that implements io.Writer with rate limiting. 50 | func NewWriterWithContext(w io.Writer, ctx context.Context) *Writer { 51 | return &Writer{ 52 | w: w, 53 | ctx: ctx, 54 | } 55 | } 56 | 57 | // SetRateLimit sets rate limit (bytes/sec) to the reader. 58 | func (s *Reader) SetRateLimit(bytesPerSec float64) { 59 | s.limiter = rate.NewLimiter(rate.Limit(bytesPerSec), burstLimit) 60 | s.limiter.AllowN(time.Now(), burstLimit) // spend initial burst 61 | } 62 | 63 | // Read reads bytes into p. 64 | func (s *Reader) Read(p []byte) (int, error) { 65 | if s.limiter == nil { 66 | return s.r.Read(p) 67 | } 68 | n, err := s.r.Read(p) 69 | if err != nil { 70 | return n, err 71 | } 72 | if err := s.limiter.WaitN(s.ctx, n); err != nil { 73 | return n, err 74 | } 75 | return n, nil 76 | } 77 | 78 | // SetRateLimit sets rate limit (bytes/sec) to the writer. 79 | func (s *Writer) SetRateLimit(bytesPerSec float64) { 80 | s.limiter = rate.NewLimiter(rate.Limit(bytesPerSec), burstLimit) 81 | s.limiter.AllowN(time.Now(), burstLimit) // spend initial burst 82 | } 83 | 84 | // Write writes bytes from p. 85 | func (s *Writer) Write(p []byte) (int, error) { 86 | if s.limiter == nil { 87 | return s.w.Write(p) 88 | } 89 | n, err := s.w.Write(p) 90 | if err != nil { 91 | return n, err 92 | } 93 | if err := s.limiter.WaitN(s.ctx, n); err != nil { 94 | return n, err 95 | } 96 | return n, err 97 | } 98 | -------------------------------------------------------------------------------- /lib/goproxy/utils/leakybuf.go: -------------------------------------------------------------------------------- 1 | // Provides leaky buffer, based on the example in Effective Go. 2 | package utils 3 | 4 | type LeakyBuf struct { 5 | bufSize int // size of each buffer 6 | freeList chan []byte 7 | } 8 | 9 | const LeakyBufSize = 2048 // data.len(2) + hmacsha1(10) + data(4096) 10 | const maxNBuf = 2048 11 | 12 | var LeakyBuffer = NewLeakyBuf(maxNBuf, LeakyBufSize) 13 | 14 | // NewLeakyBuf creates a leaky buffer which can hold at most n buffer, each 15 | // with bufSize bytes. 16 | func NewLeakyBuf(n, bufSize int) *LeakyBuf { 17 | return &LeakyBuf{ 18 | bufSize: bufSize, 19 | freeList: make(chan []byte, n), 20 | } 21 | } 22 | 23 | // Get returns a buffer from the leaky buffer or create a new buffer. 24 | func (lb *LeakyBuf) Get() (b []byte) { 25 | select { 26 | case b = <-lb.freeList: 27 | default: 28 | b = make([]byte, lb.bufSize) 29 | } 30 | return 31 | } 32 | 33 | // Put add the buffer into the free buffer pool for reuse. Panic if the buffer 34 | // size is not the same with the leaky buffer's. This is intended to expose 35 | // error usage of leaky buffer. 36 | func (lb *LeakyBuf) Put(b []byte) { 37 | if len(b) != lb.bufSize { 38 | panic("invalid buffer size that's put into leaky buffer") 39 | } 40 | select { 41 | case lb.freeList <- b: 42 | default: 43 | } 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /lib/goproxy/utils/map.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "sync" 6 | ) 7 | 8 | var SHARD_COUNT = 32 9 | 10 | // A "thread" safe map of type string:Anything. 11 | // To avoid lock bottlenecks this map is dived to several (SHARD_COUNT) map shards. 12 | type ConcurrentMap []*ConcurrentMapShared 13 | 14 | // A "thread" safe string to anything map. 15 | type ConcurrentMapShared struct { 16 | items map[string]interface{} 17 | sync.RWMutex // Read Write mutex, guards access to internal map. 18 | } 19 | 20 | // Creates a new concurrent map. 21 | func NewConcurrentMap() ConcurrentMap { 22 | m := make(ConcurrentMap, SHARD_COUNT) 23 | for i := 0; i < SHARD_COUNT; i++ { 24 | m[i] = &ConcurrentMapShared{items: make(map[string]interface{})} 25 | } 26 | return m 27 | } 28 | 29 | // Returns shard under given key 30 | func (m ConcurrentMap) GetShard(key string) *ConcurrentMapShared { 31 | return m[uint(fnv32(key))%uint(SHARD_COUNT)] 32 | } 33 | 34 | func (m ConcurrentMap) MSet(data map[string]interface{}) { 35 | for key, value := range data { 36 | shard := m.GetShard(key) 37 | shard.Lock() 38 | shard.items[key] = value 39 | shard.Unlock() 40 | } 41 | } 42 | 43 | // Sets the given value under the specified key. 44 | func (m ConcurrentMap) Set(key string, value interface{}) { 45 | // Get map shard. 46 | shard := m.GetShard(key) 47 | shard.Lock() 48 | shard.items[key] = value 49 | shard.Unlock() 50 | } 51 | 52 | // Callback to return new element to be inserted into the map 53 | // It is called while lock is held, therefore it MUST NOT 54 | // try to access other keys in same map, as it can lead to deadlock since 55 | // Go sync.RWLock is not reentrant 56 | type UpsertCb func(exist bool, valueInMap interface{}, newValue interface{}) interface{} 57 | 58 | // Insert or Update - updates existing element or inserts a new one using UpsertCb 59 | func (m ConcurrentMap) Upsert(key string, value interface{}, cb UpsertCb) (res interface{}) { 60 | shard := m.GetShard(key) 61 | shard.Lock() 62 | v, ok := shard.items[key] 63 | res = cb(ok, v, value) 64 | shard.items[key] = res 65 | shard.Unlock() 66 | return res 67 | } 68 | 69 | // Sets the given value under the specified key if no value was associated with it. 70 | func (m ConcurrentMap) SetIfAbsent(key string, value interface{}) bool { 71 | // Get map shard. 72 | shard := m.GetShard(key) 73 | shard.Lock() 74 | _, ok := shard.items[key] 75 | if !ok { 76 | shard.items[key] = value 77 | } 78 | shard.Unlock() 79 | return !ok 80 | } 81 | 82 | // Retrieves an element from map under given key. 83 | func (m ConcurrentMap) Get(key string) (interface{}, bool) { 84 | // Get shard 85 | shard := m.GetShard(key) 86 | shard.RLock() 87 | // Get item from shard. 88 | val, ok := shard.items[key] 89 | shard.RUnlock() 90 | return val, ok 91 | } 92 | 93 | // Returns the number of elements within the map. 94 | func (m ConcurrentMap) Count() int { 95 | count := 0 96 | for i := 0; i < SHARD_COUNT; i++ { 97 | shard := m[i] 98 | shard.RLock() 99 | count += len(shard.items) 100 | shard.RUnlock() 101 | } 102 | return count 103 | } 104 | 105 | // Looks up an item under specified key 106 | func (m ConcurrentMap) Has(key string) bool { 107 | // Get shard 108 | shard := m.GetShard(key) 109 | shard.RLock() 110 | // See if element is within shard. 111 | _, ok := shard.items[key] 112 | shard.RUnlock() 113 | return ok 114 | } 115 | 116 | // Removes an element from the map. 117 | func (m ConcurrentMap) Remove(key string) { 118 | // Try to get shard. 119 | shard := m.GetShard(key) 120 | shard.Lock() 121 | delete(shard.items, key) 122 | shard.Unlock() 123 | } 124 | 125 | // Removes an element from the map and returns it 126 | func (m ConcurrentMap) Pop(key string) (v interface{}, exists bool) { 127 | // Try to get shard. 128 | shard := m.GetShard(key) 129 | shard.Lock() 130 | v, exists = shard.items[key] 131 | delete(shard.items, key) 132 | shard.Unlock() 133 | return v, exists 134 | } 135 | 136 | // Checks if map is empty. 137 | func (m ConcurrentMap) IsEmpty() bool { 138 | return m.Count() == 0 139 | } 140 | 141 | // Used by the Iter & IterBuffered functions to wrap two variables together over a channel, 142 | type Tuple struct { 143 | Key string 144 | Val interface{} 145 | } 146 | 147 | // Returns an iterator which could be used in a for range loop. 148 | // 149 | // Deprecated: using IterBuffered() will get a better performence 150 | func (m ConcurrentMap) Iter() <-chan Tuple { 151 | chans := snapshot(m) 152 | ch := make(chan Tuple) 153 | go fanIn(chans, ch) 154 | return ch 155 | } 156 | 157 | // Returns a buffered iterator which could be used in a for range loop. 158 | func (m ConcurrentMap) IterBuffered() <-chan Tuple { 159 | chans := snapshot(m) 160 | total := 0 161 | for _, c := range chans { 162 | total += cap(c) 163 | } 164 | ch := make(chan Tuple, total) 165 | go fanIn(chans, ch) 166 | return ch 167 | } 168 | 169 | // Returns a array of channels that contains elements in each shard, 170 | // which likely takes a snapshot of `m`. 171 | // It returns once the size of each buffered channel is determined, 172 | // before all the channels are populated using goroutines. 173 | func snapshot(m ConcurrentMap) (chans []chan Tuple) { 174 | chans = make([]chan Tuple, SHARD_COUNT) 175 | wg := sync.WaitGroup{} 176 | wg.Add(SHARD_COUNT) 177 | // Foreach shard. 178 | for index, shard := range m { 179 | go func(index int, shard *ConcurrentMapShared) { 180 | // Foreach key, value pair. 181 | shard.RLock() 182 | chans[index] = make(chan Tuple, len(shard.items)) 183 | wg.Done() 184 | for key, val := range shard.items { 185 | chans[index] <- Tuple{key, val} 186 | } 187 | shard.RUnlock() 188 | close(chans[index]) 189 | }(index, shard) 190 | } 191 | wg.Wait() 192 | return chans 193 | } 194 | 195 | // fanIn reads elements from channels `chans` into channel `out` 196 | func fanIn(chans []chan Tuple, out chan Tuple) { 197 | wg := sync.WaitGroup{} 198 | wg.Add(len(chans)) 199 | for _, ch := range chans { 200 | go func(ch chan Tuple) { 201 | for t := range ch { 202 | out <- t 203 | } 204 | wg.Done() 205 | }(ch) 206 | } 207 | wg.Wait() 208 | close(out) 209 | } 210 | 211 | // Returns all items as map[string]interface{} 212 | func (m ConcurrentMap) Items() map[string]interface{} { 213 | tmp := make(map[string]interface{}) 214 | 215 | // Insert items to temporary map. 216 | for item := range m.IterBuffered() { 217 | tmp[item.Key] = item.Val 218 | } 219 | 220 | return tmp 221 | } 222 | 223 | // Iterator callback,called for every key,value found in 224 | // maps. RLock is held for all calls for a given shard 225 | // therefore callback sess consistent view of a shard, 226 | // but not across the shards 227 | type IterCb func(key string, v interface{}) 228 | 229 | // Callback based iterator, cheapest way to read 230 | // all elements in a map. 231 | func (m ConcurrentMap) IterCb(fn IterCb) { 232 | for idx := range m { 233 | shard := (m)[idx] 234 | shard.RLock() 235 | for key, value := range shard.items { 236 | fn(key, value) 237 | } 238 | shard.RUnlock() 239 | } 240 | } 241 | 242 | // Return all keys as []string 243 | func (m ConcurrentMap) Keys() []string { 244 | count := m.Count() 245 | ch := make(chan string, count) 246 | go func() { 247 | // Foreach shard. 248 | wg := sync.WaitGroup{} 249 | wg.Add(SHARD_COUNT) 250 | for _, shard := range m { 251 | go func(shard *ConcurrentMapShared) { 252 | // Foreach key, value pair. 253 | shard.RLock() 254 | for key := range shard.items { 255 | ch <- key 256 | } 257 | shard.RUnlock() 258 | wg.Done() 259 | }(shard) 260 | } 261 | wg.Wait() 262 | close(ch) 263 | }() 264 | 265 | // Generate keys 266 | keys := make([]string, 0, count) 267 | for k := range ch { 268 | keys = append(keys, k) 269 | } 270 | return keys 271 | } 272 | 273 | //Reviles ConcurrentMap "private" variables to json marshal. 274 | func (m ConcurrentMap) MarshalJSON() ([]byte, error) { 275 | // Create a temporary map, which will hold all item spread across shards. 276 | tmp := make(map[string]interface{}) 277 | 278 | // Insert items to temporary map. 279 | for item := range m.IterBuffered() { 280 | tmp[item.Key] = item.Val 281 | } 282 | return json.Marshal(tmp) 283 | } 284 | 285 | func fnv32(key string) uint32 { 286 | hash := uint32(2166136261) 287 | const prime32 = uint32(16777619) 288 | for i := 0; i < len(key); i++ { 289 | hash *= prime32 290 | hash ^= uint32(key[i]) 291 | } 292 | return hash 293 | } 294 | 295 | // Concurrent map uses Interface{} as its value, therefor JSON Unmarshal 296 | // will probably won't know which to type to unmarshal into, in such case 297 | // we'll end up with a value of type map[string]interface{}, In most cases this isn't 298 | // out value type, this is why we've decided to remove this functionality. 299 | 300 | // func (m *ConcurrentMap) UnmarshalJSON(b []byte) (err error) { 301 | // // Reverse process of Marshal. 302 | 303 | // tmp := make(map[string]interface{}) 304 | 305 | // // Unmarshal into a single map. 306 | // if err := json.Unmarshal(b, &tmp); err != nil { 307 | // return nil 308 | // } 309 | 310 | // // foreach key,value pair in temporary map insert into our concurrent map. 311 | // for key, val := range tmp { 312 | // m.Set(key, val) 313 | // } 314 | // return nil 315 | // } 316 | -------------------------------------------------------------------------------- /lib/goproxy/utils/serve-channel.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/tls" 5 | "crypto/x509" 6 | "errors" 7 | "fmt" 8 | kcpcfg2 "github.com/yincongcyincong/proxy-web/lib/goproxy/services/kcpcfg" 9 | logger "log" 10 | "net" 11 | "runtime/debug" 12 | "strconv" 13 | 14 | kcp "github.com/xtaci/kcp-go" 15 | ) 16 | 17 | type ServerChannel struct { 18 | ip string 19 | port int 20 | Listener *net.Listener 21 | UDPListener *net.UDPConn 22 | errAcceptHandler func(err error) 23 | log *logger.Logger 24 | } 25 | 26 | func NewServerChannel(ip string, port int, log *logger.Logger) ServerChannel { 27 | return ServerChannel{ 28 | ip: ip, 29 | port: port, 30 | log: log, 31 | errAcceptHandler: func(err error) { 32 | log.Printf("accept error , ERR:%s", err) 33 | }, 34 | } 35 | } 36 | func NewServerChannelHost(host string, log *logger.Logger) ServerChannel { 37 | h, port, _ := net.SplitHostPort(host) 38 | p, _ := strconv.Atoi(port) 39 | return ServerChannel{ 40 | ip: h, 41 | port: p, 42 | log: log, 43 | errAcceptHandler: func(err error) { 44 | log.Printf("accept error , ERR:%s", err) 45 | }, 46 | } 47 | } 48 | func (sc *ServerChannel) SetErrAcceptHandler(fn func(err error)) { 49 | sc.errAcceptHandler = fn 50 | } 51 | func (sc *ServerChannel) ListenTls(certBytes, keyBytes, caCertBytes []byte, fn func(conn net.Conn)) (err error) { 52 | sc.Listener, err = sc.listenTls(sc.ip, sc.port, certBytes, keyBytes, caCertBytes) 53 | if err == nil { 54 | go func() { 55 | defer func() { 56 | if e := recover(); e != nil { 57 | sc.log.Printf("ListenTls crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 58 | } 59 | }() 60 | for { 61 | var conn net.Conn 62 | conn, err = (*sc.Listener).Accept() 63 | if err == nil { 64 | go func() { 65 | defer func() { 66 | if e := recover(); e != nil { 67 | sc.log.Printf("tls connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 68 | } 69 | }() 70 | fn(conn) 71 | }() 72 | } else { 73 | sc.errAcceptHandler(err) 74 | (*sc.Listener).Close() 75 | break 76 | } 77 | } 78 | }() 79 | } 80 | return 81 | } 82 | func (sc *ServerChannel) listenTls(ip string, port int, certBytes, keyBytes, caCertBytes []byte) (ln *net.Listener, err error) { 83 | 84 | var cert tls.Certificate 85 | cert, err = tls.X509KeyPair(certBytes, keyBytes) 86 | if err != nil { 87 | return 88 | } 89 | clientCertPool := x509.NewCertPool() 90 | caBytes := certBytes 91 | if caCertBytes != nil { 92 | caBytes = caCertBytes 93 | } 94 | ok := clientCertPool.AppendCertsFromPEM(caBytes) 95 | if !ok { 96 | err = errors.New("failed to parse root certificate") 97 | } 98 | config := &tls.Config{ 99 | ClientCAs: clientCertPool, 100 | Certificates: []tls.Certificate{cert}, 101 | ClientAuth: tls.RequireAndVerifyClientCert, 102 | } 103 | _ln, err := tls.Listen("tcp", fmt.Sprintf("%s:%d", ip, port), config) 104 | if err == nil { 105 | ln = &_ln 106 | } 107 | return 108 | } 109 | func (sc *ServerChannel) ListenTCP(fn func(conn net.Conn)) (err error) { 110 | var l net.Listener 111 | l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sc.ip, sc.port)) 112 | if err == nil { 113 | sc.Listener = &l 114 | go func() { 115 | defer func() { 116 | if e := recover(); e != nil { 117 | sc.log.Printf("ListenTCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 118 | } 119 | }() 120 | for { 121 | var conn net.Conn 122 | conn, err = (*sc.Listener).Accept() 123 | if err == nil { 124 | go func() { 125 | defer func() { 126 | if e := recover(); e != nil { 127 | sc.log.Printf("tcp connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 128 | } 129 | }() 130 | fn(conn) 131 | }() 132 | } else { 133 | sc.errAcceptHandler(err) 134 | break 135 | } 136 | } 137 | }() 138 | } 139 | return 140 | } 141 | func (sc *ServerChannel) ListenUDP(fn func(packet []byte, localAddr, srcAddr *net.UDPAddr)) (err error) { 142 | addr := &net.UDPAddr{IP: net.ParseIP(sc.ip), Port: sc.port} 143 | l, err := net.ListenUDP("udp", addr) 144 | if err == nil { 145 | sc.UDPListener = l 146 | go func() { 147 | defer func() { 148 | if e := recover(); e != nil { 149 | sc.log.Printf("ListenUDP crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 150 | } 151 | }() 152 | for { 153 | var buf = make([]byte, 2048) 154 | n, srcAddr, err := (*sc.UDPListener).ReadFromUDP(buf) 155 | if err == nil { 156 | packet := buf[0:n] 157 | go func() { 158 | defer func() { 159 | if e := recover(); e != nil { 160 | sc.log.Printf("udp data handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 161 | } 162 | }() 163 | fn(packet, addr, srcAddr) 164 | }() 165 | } else { 166 | sc.errAcceptHandler(err) 167 | break 168 | } 169 | } 170 | }() 171 | } 172 | return 173 | } 174 | func (sc *ServerChannel) ListenKCP(config kcpcfg2.KCPConfigArgs, fn func(conn net.Conn), log *logger.Logger) (err error) { 175 | lis, err := kcp.ListenWithOptions(fmt.Sprintf("%s:%d", sc.ip, sc.port), config.Block, *config.DataShard, *config.ParityShard) 176 | if err == nil { 177 | if err = lis.SetDSCP(*config.DSCP); err != nil { 178 | log.Println("SetDSCP:", err) 179 | return 180 | } 181 | if err = lis.SetReadBuffer(*config.SockBuf); err != nil { 182 | log.Println("SetReadBuffer:", err) 183 | return 184 | } 185 | if err = lis.SetWriteBuffer(*config.SockBuf); err != nil { 186 | log.Println("SetWriteBuffer:", err) 187 | return 188 | } 189 | sc.Listener = new(net.Listener) 190 | *sc.Listener = lis 191 | go func() { 192 | defer func() { 193 | if e := recover(); e != nil { 194 | sc.log.Printf("ListenKCP crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 195 | } 196 | }() 197 | for { 198 | //var conn net.Conn 199 | conn, err := lis.AcceptKCP() 200 | if err == nil { 201 | go func() { 202 | defer func() { 203 | if e := recover(); e != nil { 204 | sc.log.Printf("kcp connection handler crashed , err : %s , \ntrace:%s", e, string(debug.Stack())) 205 | } 206 | }() 207 | conn.SetStreamMode(true) 208 | conn.SetWriteDelay(true) 209 | conn.SetNoDelay(*config.NoDelay, *config.Interval, *config.Resend, *config.NoCongestion) 210 | conn.SetMtu(*config.MTU) 211 | conn.SetWindowSize(*config.SndWnd, *config.RcvWnd) 212 | conn.SetACKNoDelay(*config.AckNodelay) 213 | if *config.NoComp { 214 | fn(conn) 215 | } else { 216 | cconn := NewCompStream(conn) 217 | fn(cconn) 218 | } 219 | }() 220 | } else { 221 | sc.errAcceptHandler(err) 222 | break 223 | } 224 | } 225 | }() 226 | } 227 | return 228 | } 229 | -------------------------------------------------------------------------------- /lib/goproxy/utils/sni/sni.go: -------------------------------------------------------------------------------- 1 | package sni 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "errors" 7 | "io" 8 | "net" 9 | ) 10 | 11 | func ServerNameFromBytes(data []byte) (sn string, err error) { 12 | reader := bytes.NewReader(data) 13 | bufferedReader := bufio.NewReader(reader) 14 | c := bufferedConn{bufferedReader, nil, nil} 15 | sn, _, err = ServerNameFromConn(c) 16 | return 17 | } 18 | 19 | type bufferedConn struct { 20 | r *bufio.Reader 21 | rout io.Reader 22 | net.Conn 23 | } 24 | 25 | func newBufferedConn(c net.Conn) bufferedConn { 26 | return bufferedConn{bufio.NewReader(c), nil, c} 27 | } 28 | 29 | func (b bufferedConn) Peek(n int) ([]byte, error) { 30 | return b.r.Peek(n) 31 | } 32 | 33 | func (b bufferedConn) Read(p []byte) (int, error) { 34 | if b.rout != nil { 35 | return b.rout.Read(p) 36 | } 37 | return b.r.Read(p) 38 | } 39 | 40 | var malformedError = errors.New("malformed client hello") 41 | 42 | func getHello(b []byte) (string, error) { 43 | rest := b[5:] 44 | 45 | if len(rest) == 0 { 46 | return "", malformedError 47 | } 48 | 49 | current := 0 50 | handshakeType := rest[0] 51 | current += 1 52 | if handshakeType != 0x1 { 53 | return "", errors.New("Not a ClientHello") 54 | } 55 | 56 | // Skip over another length 57 | current += 3 58 | // Skip over protocolversion 59 | current += 2 60 | // Skip over random number 61 | current += 4 + 28 62 | 63 | if current > len(rest) { 64 | return "", malformedError 65 | } 66 | 67 | // Skip over session ID 68 | sessionIDLength := int(rest[current]) 69 | current += 1 70 | current += sessionIDLength 71 | 72 | if current+1 > len(rest) { 73 | return "", malformedError 74 | } 75 | 76 | cipherSuiteLength := (int(rest[current]) << 8) + int(rest[current+1]) 77 | current += 2 78 | current += cipherSuiteLength 79 | 80 | if current > len(rest) { 81 | return "", malformedError 82 | } 83 | compressionMethodLength := int(rest[current]) 84 | current += 1 85 | current += compressionMethodLength 86 | 87 | if current > len(rest) { 88 | return "", errors.New("no extensions") 89 | } 90 | 91 | current += 2 92 | 93 | hostname := "" 94 | for current+4 < len(rest) && hostname == "" { 95 | extensionType := (int(rest[current]) << 8) + int(rest[current+1]) 96 | current += 2 97 | 98 | extensionDataLength := (int(rest[current]) << 8) + int(rest[current+1]) 99 | current += 2 100 | 101 | if extensionType == 0 { 102 | 103 | // Skip over number of names as we're assuming there's just one 104 | current += 2 105 | if current > len(rest) { 106 | return "", malformedError 107 | } 108 | 109 | nameType := rest[current] 110 | current += 1 111 | if nameType != 0 { 112 | return "", errors.New("Not a hostname") 113 | } 114 | if current+1 > len(rest) { 115 | return "", malformedError 116 | } 117 | nameLen := (int(rest[current]) << 8) + int(rest[current+1]) 118 | current += 2 119 | if current+nameLen > len(rest) { 120 | return "", malformedError 121 | } 122 | hostname = string(rest[current : current+nameLen]) 123 | } 124 | 125 | current += extensionDataLength 126 | } 127 | if hostname == "" { 128 | return "", errors.New("No hostname") 129 | } 130 | return hostname, nil 131 | 132 | } 133 | 134 | func getHelloBytes(c bufferedConn) ([]byte, error) { 135 | b, err := c.Peek(5) 136 | if err != nil { 137 | return []byte{}, err 138 | } 139 | 140 | if b[0] != 0x16 { 141 | return []byte{}, errors.New("not TLS") 142 | } 143 | 144 | restLengthBytes := b[3:] 145 | restLength := (int(restLengthBytes[0]) << 8) + int(restLengthBytes[1]) 146 | 147 | return c.Peek(5 + restLength) 148 | 149 | } 150 | 151 | func getServername(c bufferedConn) (string, []byte, error) { 152 | all, err := getHelloBytes(c) 153 | if err != nil { 154 | return "", nil, err 155 | } 156 | name, err := getHello(all) 157 | if err != nil { 158 | return "", nil, err 159 | } 160 | return name, all, err 161 | 162 | } 163 | 164 | // Uses SNI to get the name of the server from the connection. Returns the ServerName and a buffered connection that will not have been read off of. 165 | func ServerNameFromConn(c net.Conn) (string, net.Conn, error) { 166 | bufconn := newBufferedConn(c) 167 | sn, helloBytes, err := getServername(bufconn) 168 | if err != nil { 169 | return "", nil, err 170 | } 171 | bufconn.rout = io.MultiReader(bytes.NewBuffer(helloBytes), c) 172 | return sn, bufconn, nil 173 | } 174 | -------------------------------------------------------------------------------- /lib/goproxy/utils/socks/client.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "net" 9 | "strconv" 10 | "time" 11 | ) 12 | 13 | var socks5Errors = []string{ 14 | "", 15 | "general failure", 16 | "connection forbidden", 17 | "network unreachable", 18 | "host unreachable", 19 | "connection refused", 20 | "TTL expired", 21 | "command not supported", 22 | "address type not supported", 23 | } 24 | 25 | type Auth struct { 26 | User, Password string 27 | } 28 | type ClientConn struct { 29 | user string 30 | password string 31 | conn *net.Conn 32 | header []byte 33 | timeout time.Duration 34 | addr string 35 | network string 36 | UDPAddr string 37 | } 38 | 39 | // SOCKS5 returns a Dialer that makes SOCKSv5 connections to the given address 40 | // with an optional username and password. See RFC 1928 and RFC 1929. 41 | // target must be a canonical address with a host and port. 42 | // network : tcp udp 43 | func NewClientConn(conn *net.Conn, network, target string, timeout time.Duration, auth *Auth, header []byte) *ClientConn { 44 | s := &ClientConn{ 45 | conn: conn, 46 | network: network, 47 | timeout: timeout, 48 | } 49 | if auth != nil { 50 | s.user = auth.User 51 | s.password = auth.Password 52 | } 53 | if header != nil && len(header) > 0 { 54 | s.header = header 55 | } 56 | if network == "udp" && target == "" { 57 | target = "0.0.0.0:1" 58 | } 59 | s.addr = target 60 | return s 61 | } 62 | 63 | // connect takes an existing connection to a socks5 proxy server, 64 | // and commands the server to extend that connection to target, 65 | // which must be a canonical address with a host and port. 66 | func (s *ClientConn) Handshake() error { 67 | host, portStr, err := net.SplitHostPort(s.addr) 68 | if err != nil { 69 | return err 70 | } 71 | port, err := strconv.Atoi(portStr) 72 | if err != nil { 73 | return errors.New("proxy: failed to parse port number: " + portStr) 74 | } 75 | if s.network == "tcp" && (port < 1 || port > 0xffff) { 76 | return errors.New("proxy: port number out of range: " + portStr) 77 | } 78 | 79 | if err := s.handshake(host); err != nil { 80 | return err 81 | } 82 | buf := []byte{} 83 | if s.network == "tcp" { 84 | buf = append(buf, VERSION_V5, CMD_CONNECT, 0 /* reserved */) 85 | 86 | } else { 87 | buf = append(buf, VERSION_V5, CMD_ASSOCIATE, 0 /* reserved */) 88 | } 89 | if ip := net.ParseIP(host); ip != nil { 90 | if ip4 := ip.To4(); ip4 != nil { 91 | buf = append(buf, ATYP_IPV4) 92 | ip = ip4 93 | } else { 94 | buf = append(buf, ATYP_IPV6) 95 | } 96 | buf = append(buf, ip...) 97 | } else { 98 | if len(host) > 255 { 99 | return errors.New("proxy: destination host name too long: " + host) 100 | } 101 | buf = append(buf, ATYP_DOMAIN) 102 | buf = append(buf, byte(len(host))) 103 | buf = append(buf, host...) 104 | } 105 | buf = append(buf, byte(port>>8), byte(port)) 106 | (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 107 | if _, err := (*s.conn).Write(buf); err != nil { 108 | return errors.New("proxy: failed to write connect request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) 109 | } 110 | (*s.conn).SetDeadline(time.Time{}) 111 | (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 112 | if _, err := io.ReadFull((*s.conn), buf[:4]); err != nil { 113 | return errors.New("proxy: failed to read connect reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 114 | } 115 | (*s.conn).SetDeadline(time.Time{}) 116 | failure := "unknown error" 117 | if int(buf[1]) < len(socks5Errors) { 118 | failure = socks5Errors[buf[1]] 119 | } 120 | 121 | if len(failure) > 0 { 122 | return errors.New("proxy: SOCKS5 proxy at " + s.addr + " failed to connect: " + failure) 123 | } 124 | 125 | bytesToDiscard := 0 126 | switch buf[3] { 127 | case ATYP_IPV4: 128 | bytesToDiscard = net.IPv4len 129 | case ATYP_IPV6: 130 | bytesToDiscard = net.IPv6len 131 | case ATYP_DOMAIN: 132 | (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 133 | _, err := io.ReadFull((*s.conn), buf[:1]) 134 | (*s.conn).SetDeadline(time.Time{}) 135 | if err != nil { 136 | return errors.New("proxy: failed to read domain length from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 137 | } 138 | bytesToDiscard = int(buf[0]) 139 | default: 140 | return errors.New("proxy: got unknown address type " + strconv.Itoa(int(buf[3])) + " from SOCKS5 proxy at " + s.addr) 141 | } 142 | 143 | if cap(buf) < bytesToDiscard { 144 | buf = make([]byte, bytesToDiscard) 145 | } else { 146 | buf = buf[:bytesToDiscard] 147 | } 148 | (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 149 | if _, err := io.ReadFull((*s.conn), buf); err != nil { 150 | return errors.New("proxy: failed to read address from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 151 | } 152 | (*s.conn).SetDeadline(time.Time{}) 153 | var ip net.IP 154 | ip = buf 155 | ipStr := "" 156 | if bytesToDiscard == net.IPv4len || bytesToDiscard == net.IPv6len { 157 | if ipv4 := ip.To4(); ipv4 != nil { 158 | ipStr = ipv4.String() 159 | } else { 160 | ipStr = ip.To16().String() 161 | } 162 | } 163 | //log.Printf("%v", ipStr) 164 | // Also need to discard the port number 165 | (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 166 | if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil { 167 | return errors.New("proxy: failed to read port from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 168 | } 169 | p := binary.BigEndian.Uint16([]byte{buf[0], buf[1]}) 170 | //log.Printf("%v", p) 171 | s.UDPAddr = net.JoinHostPort(ipStr, fmt.Sprintf("%d", p)) 172 | //log.Printf("%v", s.udpAddr) 173 | (*s.conn).SetDeadline(time.Time{}) 174 | return nil 175 | } 176 | func (s *ClientConn) SendUDP(data []byte, addr string) (respData []byte, err error) { 177 | 178 | c, err := net.DialTimeout("udp", s.UDPAddr, s.timeout) 179 | if err != nil { 180 | return 181 | } 182 | conn := c.(*net.UDPConn) 183 | 184 | p := NewPacketUDP() 185 | p.Build(addr, data) 186 | conn.SetDeadline(time.Now().Add(s.timeout)) 187 | conn.Write(p.Bytes()) 188 | conn.SetDeadline(time.Time{}) 189 | 190 | buf := make([]byte, 1024) 191 | conn.SetDeadline(time.Now().Add(s.timeout)) 192 | n, _, err := conn.ReadFrom(buf) 193 | conn.SetDeadline(time.Time{}) 194 | if err != nil { 195 | return 196 | } 197 | respData = buf[:n] 198 | return 199 | } 200 | func (s *ClientConn) handshake(host string) error { 201 | 202 | // the size here is just an estimate 203 | buf := make([]byte, 0, 6+len(host)) 204 | 205 | buf = append(buf, VERSION_V5) 206 | if len(s.user) > 0 && len(s.user) < 256 && len(s.password) < 256 { 207 | buf = append(buf, 2 /* num auth methods */, Method_NO_AUTH, Method_USER_PASS) 208 | } else { 209 | buf = append(buf, 1 /* num auth methods */, Method_NO_AUTH) 210 | } 211 | (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 212 | if _, err := (*s.conn).Write(buf); err != nil { 213 | return errors.New("proxy: failed to write greeting to SOCKS5 proxy at " + s.addr + ": " + err.Error()) 214 | } 215 | (*s.conn).SetDeadline(time.Time{}) 216 | 217 | (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 218 | if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil { 219 | return errors.New("proxy: failed to read greeting from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 220 | } 221 | (*s.conn).SetDeadline(time.Time{}) 222 | 223 | if buf[0] != 5 { 224 | return errors.New("proxy: SOCKS5 proxy at " + s.addr + " has unexpected version " + strconv.Itoa(int(buf[0]))) 225 | } 226 | if buf[1] == 0xff { 227 | return errors.New("proxy: SOCKS5 proxy at " + s.addr + " requires authentication") 228 | } 229 | 230 | // See RFC 1929 231 | if buf[1] == Method_USER_PASS { 232 | buf = buf[:0] 233 | buf = append(buf, 1 /* password protocol version */) 234 | buf = append(buf, uint8(len(s.user))) 235 | buf = append(buf, s.user...) 236 | buf = append(buf, uint8(len(s.password))) 237 | buf = append(buf, s.password...) 238 | (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 239 | if _, err := (*s.conn).Write(buf); err != nil { 240 | return errors.New("proxy: failed to write authentication request to SOCKS5 proxy at " + s.addr + ": " + err.Error()) 241 | } 242 | (*s.conn).SetDeadline(time.Time{}) 243 | (*s.conn).SetDeadline(time.Now().Add(s.timeout)) 244 | if _, err := io.ReadFull((*s.conn), buf[:2]); err != nil { 245 | return errors.New("proxy: failed to read authentication reply from SOCKS5 proxy at " + s.addr + ": " + err.Error()) 246 | } 247 | (*s.conn).SetDeadline(time.Time{}) 248 | if buf[1] != 0 { 249 | return errors.New("proxy: SOCKS5 proxy at " + s.addr + " rejected username/password") 250 | } 251 | } 252 | return nil 253 | } 254 | -------------------------------------------------------------------------------- /lib/goproxy/utils/socks/server.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | import ( 4 | "fmt" 5 | utils2 "github.com/yincongcyincong/proxy-web/lib/goproxy/utils" 6 | "net" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | const ( 12 | Method_NO_AUTH = uint8(0x00) 13 | Method_GSSAPI = uint8(0x01) 14 | Method_USER_PASS = uint8(0x02) 15 | Method_IANA = uint8(0x7F) 16 | Method_RESVERVE = uint8(0x80) 17 | Method_NONE_ACCEPTABLE = uint8(0xFF) 18 | VERSION_V5 = uint8(0x05) 19 | CMD_CONNECT = uint8(0x01) 20 | CMD_BIND = uint8(0x02) 21 | CMD_ASSOCIATE = uint8(0x03) 22 | ATYP_IPV4 = uint8(0x01) 23 | ATYP_DOMAIN = uint8(0x03) 24 | ATYP_IPV6 = uint8(0x04) 25 | REP_SUCCESS = uint8(0x00) 26 | REP_REQ_FAIL = uint8(0x01) 27 | REP_RULE_FORBIDDEN = uint8(0x02) 28 | REP_NETWOR_UNREACHABLE = uint8(0x03) 29 | REP_HOST_UNREACHABLE = uint8(0x04) 30 | REP_CONNECTION_REFUSED = uint8(0x05) 31 | REP_TTL_TIMEOUT = uint8(0x06) 32 | REP_CMD_UNSUPPORTED = uint8(0x07) 33 | REP_ATYP_UNSUPPORTED = uint8(0x08) 34 | REP_UNKNOWN = uint8(0x09) 35 | RSV = uint8(0x00) 36 | ) 37 | 38 | var ( 39 | ZERO_IP = []byte{0x00, 0x00, 0x00, 0x00} 40 | ZERO_PORT = []byte{0x00, 0x00} 41 | ) 42 | 43 | type ServerConn struct { 44 | target string 45 | user string 46 | password string 47 | conn *net.Conn 48 | timeout time.Duration 49 | auth *utils2.BasicAuth 50 | header []byte 51 | ver uint8 52 | //method 53 | methodsCount uint8 54 | methods []uint8 55 | method uint8 56 | //request 57 | cmd uint8 58 | reserve uint8 59 | addressType uint8 60 | dstAddr string 61 | dstPort string 62 | dstHost string 63 | UDPConnListener *net.UDPConn 64 | enableUDP bool 65 | udpIP string 66 | } 67 | 68 | func NewServerConn(conn *net.Conn, timeout time.Duration, auth *utils2.BasicAuth, enableUDP bool, udpHost string, header []byte) *ServerConn { 69 | 70 | s := &ServerConn{ 71 | conn: conn, 72 | timeout: timeout, 73 | auth: auth, 74 | header: header, 75 | ver: VERSION_V5, 76 | enableUDP: enableUDP, 77 | udpIP: udpHost, 78 | } 79 | return s 80 | 81 | } 82 | func (s *ServerConn) Close() { 83 | utils2.CloseConn(s.conn) 84 | } 85 | func (s *ServerConn) AuthData() Auth { 86 | return Auth{s.user, s.password} 87 | } 88 | func (s *ServerConn) IsUDP() bool { 89 | return s.cmd == CMD_ASSOCIATE 90 | } 91 | func (s *ServerConn) IsTCP() bool { 92 | return s.cmd == CMD_CONNECT 93 | } 94 | func (s *ServerConn) Method() uint8 { 95 | return s.method 96 | } 97 | func (s *ServerConn) Target() string { 98 | return s.target 99 | } 100 | func (s *ServerConn) Handshake() (err error) { 101 | remoteAddr := (*s.conn).RemoteAddr() 102 | //协商开始 103 | //method select request 104 | var methodReq MethodsRequest 105 | (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) 106 | 107 | methodReq, e := NewMethodsRequest((*s.conn), s.header) 108 | (*s.conn).SetReadDeadline(time.Time{}) 109 | if e != nil { 110 | (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) 111 | methodReq.Reply(Method_NONE_ACCEPTABLE) 112 | (*s.conn).SetReadDeadline(time.Time{}) 113 | err = fmt.Errorf("new methods request fail,ERR: %s", e) 114 | return 115 | } 116 | //log.Printf("%v,s.auth == %v && methodReq.Select(Method_NO_AUTH) %v", methodReq.methods, s.auth, methodReq.Select(Method_NO_AUTH)) 117 | if s.auth == nil && methodReq.Select(Method_NO_AUTH) && !methodReq.Select(Method_USER_PASS) { 118 | // if !methodReq.Select(Method_NO_AUTH) { 119 | // (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) 120 | // methodReq.Reply(Method_NONE_ACCEPTABLE) 121 | // (*s.conn).SetReadDeadline(time.Time{}) 122 | // err = fmt.Errorf("none method found : Method_NO_AUTH") 123 | // return 124 | // } 125 | s.method = Method_NO_AUTH 126 | //method select reply 127 | (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) 128 | err = methodReq.Reply(Method_NO_AUTH) 129 | (*s.conn).SetReadDeadline(time.Time{}) 130 | if err != nil { 131 | err = fmt.Errorf("reply answer data fail,ERR: %s", err) 132 | return 133 | } 134 | // err = fmt.Errorf("% x", methodReq.Bytes()) 135 | } else { 136 | //auth 137 | if !methodReq.Select(Method_USER_PASS) { 138 | (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) 139 | methodReq.Reply(Method_NONE_ACCEPTABLE) 140 | (*s.conn).SetReadDeadline(time.Time{}) 141 | err = fmt.Errorf("none method found : Method_USER_PASS") 142 | return 143 | } 144 | s.method = Method_USER_PASS 145 | //method reply need auth 146 | (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) 147 | err = methodReq.Reply(Method_USER_PASS) 148 | (*s.conn).SetReadDeadline(time.Time{}) 149 | if err != nil { 150 | err = fmt.Errorf("reply answer data fail,ERR: %s", err) 151 | return 152 | } 153 | //read auth 154 | buf := make([]byte, 500) 155 | var n int 156 | (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) 157 | n, err = (*s.conn).Read(buf) 158 | (*s.conn).SetReadDeadline(time.Time{}) 159 | if err != nil { 160 | err = fmt.Errorf("read auth info fail,ERR: %s", err) 161 | return 162 | } 163 | r := buf[:n] 164 | s.user = string(r[2 : r[1]+2]) 165 | s.password = string(r[2+r[1]+1:]) 166 | //err = fmt.Errorf("user:%s,pass:%s", user, pass) 167 | //auth 168 | _addr := strings.Split(remoteAddr.String(), ":") 169 | if s.auth == nil || s.auth.CheckUserPass(s.user, s.password, _addr[0], "") { 170 | (*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout))) 171 | _, err = (*s.conn).Write([]byte{0x01, 0x00}) 172 | (*s.conn).SetDeadline(time.Time{}) 173 | if err != nil { 174 | err = fmt.Errorf("answer auth success to %s fail,ERR: %s", remoteAddr, err) 175 | return 176 | } 177 | } else { 178 | (*s.conn).SetDeadline(time.Now().Add(time.Millisecond * time.Duration(s.timeout))) 179 | _, err = (*s.conn).Write([]byte{0x01, 0x01}) 180 | (*s.conn).SetDeadline(time.Time{}) 181 | if err != nil { 182 | err = fmt.Errorf("answer auth fail to %s fail,ERR: %s", remoteAddr, err) 183 | return 184 | } 185 | err = fmt.Errorf("auth fail from %s", remoteAddr) 186 | return 187 | } 188 | } 189 | //request detail 190 | (*s.conn).SetReadDeadline(time.Now().Add(time.Second * s.timeout)) 191 | request, e := NewRequest(*s.conn) 192 | (*s.conn).SetReadDeadline(time.Time{}) 193 | if e != nil { 194 | err = fmt.Errorf("read request data fail,ERR: %s", e) 195 | return 196 | } 197 | //协商结束 198 | 199 | switch request.CMD() { 200 | case CMD_BIND: 201 | err = request.TCPReply(REP_UNKNOWN) 202 | if err != nil { 203 | err = fmt.Errorf("TCPReply REP_UNKNOWN to %s fail,ERR: %s", remoteAddr, err) 204 | return 205 | } 206 | err = fmt.Errorf("cmd bind not supported, form: %s", remoteAddr) 207 | return 208 | case CMD_CONNECT: 209 | err = request.TCPReply(REP_SUCCESS) 210 | if err != nil { 211 | err = fmt.Errorf("TCPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err) 212 | return 213 | } 214 | case CMD_ASSOCIATE: 215 | if !s.enableUDP { 216 | request.UDPReply(REP_UNKNOWN, "0.0.0.0:0") 217 | if err != nil { 218 | err = fmt.Errorf("UDPReply REP_UNKNOWN to %s fail,ERR: %s", remoteAddr, err) 219 | return 220 | } 221 | err = fmt.Errorf("cmd associate not supported, form: %s", remoteAddr) 222 | return 223 | } 224 | a, _ := net.ResolveUDPAddr("udp", ":0") 225 | s.UDPConnListener, err = net.ListenUDP("udp", a) 226 | if err != nil { 227 | request.UDPReply(REP_UNKNOWN, "0.0.0.0:0") 228 | err = fmt.Errorf("udp bind fail,ERR: %s , for %s", err, remoteAddr) 229 | return 230 | } 231 | _, port, _ := net.SplitHostPort(s.UDPConnListener.LocalAddr().String()) 232 | err = request.UDPReply(REP_SUCCESS, net.JoinHostPort(s.udpIP, port)) 233 | if err != nil { 234 | err = fmt.Errorf("UDPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err) 235 | return 236 | } 237 | 238 | } 239 | 240 | //fill socks info 241 | s.target = request.Addr() 242 | s.methodsCount = methodReq.MethodsCount() 243 | s.methods = methodReq.Methods() 244 | s.cmd = request.CMD() 245 | s.reserve = request.reserve 246 | s.addressType = request.addressType 247 | s.dstAddr = request.dstAddr 248 | s.dstHost = request.dstHost 249 | s.dstPort = request.dstPort 250 | return 251 | } 252 | -------------------------------------------------------------------------------- /lib/goproxy/utils/socks/structs.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "net" 10 | "strconv" 11 | ) 12 | 13 | type Request struct { 14 | ver uint8 15 | cmd uint8 16 | reserve uint8 17 | addressType uint8 18 | dstAddr string 19 | dstPort string 20 | dstHost string 21 | bytes []byte 22 | rw io.ReadWriter 23 | } 24 | 25 | func NewRequest(rw io.ReadWriter, header ...[]byte) (req Request, err interface{}) { 26 | var b = make([]byte, 1024) 27 | var n int 28 | req = Request{rw: rw} 29 | if header != nil && len(header) == 1 && len(header[0]) > 1 { 30 | b = header[0] 31 | n = len(header[0]) 32 | } else { 33 | n, err = rw.Read(b[:]) 34 | if err != nil { 35 | err = fmt.Errorf("read req data fail,ERR: %s", err) 36 | return 37 | } 38 | } 39 | req.ver = uint8(b[0]) 40 | req.cmd = uint8(b[1]) 41 | req.reserve = uint8(b[2]) 42 | req.addressType = uint8(b[3]) 43 | if b[0] != 0x5 { 44 | err = fmt.Errorf("sosck version supported") 45 | req.TCPReply(REP_REQ_FAIL) 46 | return 47 | } 48 | switch b[3] { 49 | case 0x01: //IP V4 50 | req.dstHost = net.IPv4(b[4], b[5], b[6], b[7]).String() 51 | case 0x03: //域名 52 | req.dstHost = string(b[5 : n-2]) //b[4]表示域名的长度 53 | case 0x04: //IP V6 54 | req.dstHost = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String() 55 | } 56 | req.dstPort = strconv.Itoa(int(b[n-2])<<8 | int(b[n-1])) 57 | req.dstAddr = net.JoinHostPort(req.dstHost, req.dstPort) 58 | req.bytes = b[:n] 59 | return 60 | } 61 | func (s *Request) Bytes() []byte { 62 | return s.bytes 63 | } 64 | func (s *Request) Addr() string { 65 | return s.dstAddr 66 | } 67 | func (s *Request) Host() string { 68 | return s.dstHost 69 | } 70 | func (s *Request) Port() string { 71 | return s.dstPort 72 | } 73 | func (s *Request) AType() uint8 { 74 | return s.addressType 75 | } 76 | func (s *Request) CMD() uint8 { 77 | return s.cmd 78 | } 79 | 80 | func (s *Request) TCPReply(rep uint8) (err error) { 81 | _, err = s.rw.Write(s.NewReply(rep, "0.0.0.0:0")) 82 | return 83 | } 84 | func (s *Request) UDPReply(rep uint8, addr string) (err error) { 85 | _, err = s.rw.Write(s.NewReply(rep, addr)) 86 | return 87 | } 88 | func (s *Request) NewReply(rep uint8, addr string) []byte { 89 | var response bytes.Buffer 90 | host, port, _ := net.SplitHostPort(addr) 91 | ip := net.ParseIP(host) 92 | ipb := ip.To4() 93 | atyp := ATYP_IPV4 94 | ipv6 := ip.To16() 95 | zeroiIPv6 := fmt.Sprintf("%d%d%d%d%d%d%d%d%d%d%d%d", 96 | ipv6[0], ipv6[1], ipv6[2], ipv6[3], 97 | ipv6[4], ipv6[5], ipv6[6], ipv6[7], 98 | ipv6[8], ipv6[9], ipv6[10], ipv6[11], 99 | ) 100 | if ipb == nil && ipv6 != nil && "0000000000255255" != zeroiIPv6 { 101 | atyp = ATYP_IPV6 102 | ipb = ip.To16() 103 | } 104 | porti, _ := strconv.Atoi(port) 105 | portb := make([]byte, 2) 106 | binary.BigEndian.PutUint16(portb, uint16(porti)) 107 | // log.Printf("atyp : %v", atyp) 108 | // log.Printf("ip : %v", []byte(ip)) 109 | response.WriteByte(VERSION_V5) 110 | response.WriteByte(rep) 111 | response.WriteByte(RSV) 112 | response.WriteByte(atyp) 113 | response.Write(ipb) 114 | response.Write(portb) 115 | return response.Bytes() 116 | } 117 | 118 | type MethodsRequest struct { 119 | ver uint8 120 | methodsCount uint8 121 | methods []uint8 122 | bytes []byte 123 | rw *io.ReadWriter 124 | } 125 | 126 | func NewMethodsRequest(r io.ReadWriter, header ...[]byte) (s MethodsRequest, err interface{}) { 127 | defer func() { 128 | if err == nil { 129 | err = recover() 130 | } 131 | }() 132 | s = MethodsRequest{} 133 | s.rw = &r 134 | var buf = make([]byte, 300) 135 | var n int 136 | if header != nil && len(header) == 1 && len(header[0]) > 1 { 137 | buf = header[0] 138 | n = len(header[0]) 139 | } else { 140 | n, err = r.Read(buf) 141 | if err != nil { 142 | return 143 | } 144 | } 145 | if buf[0] != 0x05 { 146 | err = fmt.Errorf("socks version not supported") 147 | return 148 | } 149 | if n != int(buf[1])+int(2) { 150 | err = fmt.Errorf("socks methods data length error") 151 | return 152 | } 153 | s.ver = buf[0] 154 | s.methodsCount = buf[1] 155 | s.methods = buf[2:n] 156 | s.bytes = buf[:n] 157 | return 158 | } 159 | func (s *MethodsRequest) Version() uint8 { 160 | return s.ver 161 | } 162 | func (s *MethodsRequest) MethodsCount() uint8 { 163 | return s.methodsCount 164 | } 165 | func (s *MethodsRequest) Methods() []uint8 { 166 | return s.methods 167 | } 168 | func (s *MethodsRequest) Select(method uint8) bool { 169 | for _, m := range s.methods { 170 | if m == method { 171 | return true 172 | } 173 | } 174 | return false 175 | } 176 | func (s *MethodsRequest) Reply(method uint8) (err error) { 177 | _, err = (*s.rw).Write([]byte{byte(VERSION_V5), byte(method)}) 178 | return 179 | } 180 | func (s *MethodsRequest) Bytes() []byte { 181 | return s.bytes 182 | } 183 | 184 | func ParseUDPPacket(b []byte) (p UDPPacket, err error) { 185 | p = UDPPacket{} 186 | p.frag = uint8(b[2]) 187 | p.bytes = b 188 | if p.frag != 0 { 189 | err = fmt.Errorf("FRAG only support for 0 , %v ,%v", p.frag, b[:4]) 190 | return 191 | } 192 | portIndex := 0 193 | p.atype = b[3] 194 | switch p.atype { 195 | case ATYP_IPV4: //IP V4 196 | p.dstHost = net.IPv4(b[4], b[5], b[6], b[7]).String() 197 | portIndex = 8 198 | case ATYP_DOMAIN: //域名 199 | domainLen := uint8(b[4]) 200 | p.dstHost = string(b[5 : 5+domainLen]) //b[4]表示域名的长度 201 | portIndex = int(5 + domainLen) 202 | case ATYP_IPV6: //IP V6 203 | p.dstHost = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String() 204 | portIndex = 20 205 | } 206 | p.dstPort = strconv.Itoa(int(b[portIndex])<<8 | int(b[portIndex+1])) 207 | p.data = b[portIndex+2:] 208 | p.header = b[:portIndex+2] 209 | return 210 | } 211 | 212 | type UDPPacket struct { 213 | rsv uint16 214 | frag uint8 215 | atype uint8 216 | dstHost string 217 | dstPort string 218 | data []byte 219 | header []byte 220 | bytes []byte 221 | } 222 | 223 | func (s *UDPPacket) Header() []byte { 224 | return s.header 225 | } 226 | func (s *UDPPacket) NewReply(data []byte) []byte { 227 | var buf bytes.Buffer 228 | buf.Write(s.header) 229 | buf.Write(data) 230 | return buf.Bytes() 231 | } 232 | func (s *UDPPacket) Host() string { 233 | return s.dstHost 234 | } 235 | 236 | func (s *UDPPacket) Port() string { 237 | return s.dstPort 238 | } 239 | func (s *UDPPacket) Data() []byte { 240 | return s.data 241 | } 242 | 243 | type PacketUDP struct { 244 | rsv uint16 245 | frag uint8 246 | atype uint8 247 | dstHost string 248 | dstPort string 249 | data []byte 250 | } 251 | 252 | func NewPacketUDP() (p PacketUDP) { 253 | return PacketUDP{} 254 | } 255 | func (p *PacketUDP) Build(destAddr string, data []byte) (err error) { 256 | host, port, err := net.SplitHostPort(destAddr) 257 | if err != nil { 258 | return 259 | } 260 | p.rsv = 0 261 | p.frag = 0 262 | p.dstHost = host 263 | p.dstPort = port 264 | p.atype = ATYP_IPV4 265 | if ip := net.ParseIP(host); ip != nil { 266 | if ip4 := ip.To4(); ip4 != nil { 267 | p.atype = ATYP_IPV4 268 | ip = ip4 269 | } else { 270 | p.atype = ATYP_IPV6 271 | } 272 | } else { 273 | if len(host) > 255 { 274 | err = errors.New("proxy: destination host name too long: " + host) 275 | return 276 | } 277 | p.atype = ATYP_DOMAIN 278 | } 279 | p.data = data 280 | 281 | return 282 | } 283 | func (p *PacketUDP) Parse(b []byte) (err error) { 284 | p.frag = uint8(b[2]) 285 | if p.frag != 0 { 286 | err = fmt.Errorf("FRAG only support for 0 , %v ,%v", p.frag, b[:4]) 287 | return 288 | } 289 | portIndex := 0 290 | p.atype = b[3] 291 | switch p.atype { 292 | case ATYP_IPV4: //IP V4 293 | p.dstHost = net.IPv4(b[4], b[5], b[6], b[7]).String() 294 | portIndex = 8 295 | case ATYP_DOMAIN: //域名 296 | domainLen := uint8(b[4]) 297 | p.dstHost = string(b[5 : 5+domainLen]) //b[4]表示域名的长度 298 | portIndex = int(5 + domainLen) 299 | case ATYP_IPV6: //IP V6 300 | p.dstHost = net.IP{b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11], b[12], b[13], b[14], b[15], b[16], b[17], b[18], b[19]}.String() 301 | portIndex = 20 302 | } 303 | p.dstPort = strconv.Itoa(int(b[portIndex])<<8 | int(b[portIndex+1])) 304 | p.data = b[portIndex+2:] 305 | return 306 | } 307 | func (p *PacketUDP) Header() []byte { 308 | header := new(bytes.Buffer) 309 | header.Write([]byte{0x00, 0x00, p.frag, p.atype}) 310 | if p.atype == ATYP_IPV4 { 311 | ip := net.ParseIP(p.dstHost) 312 | header.Write(ip.To4()) 313 | } else if p.atype == ATYP_IPV6 { 314 | ip := net.ParseIP(p.dstHost) 315 | header.Write(ip.To16()) 316 | } else if p.atype == ATYP_DOMAIN { 317 | hBytes := []byte(p.dstHost) 318 | header.WriteByte(byte(len(hBytes))) 319 | header.Write(hBytes) 320 | } 321 | port, _ := strconv.ParseUint(p.dstPort, 10, 64) 322 | portBytes := new(bytes.Buffer) 323 | binary.Write(portBytes, binary.BigEndian, port) 324 | header.Write(portBytes.Bytes()[portBytes.Len()-2:]) 325 | return header.Bytes() 326 | } 327 | func (p *PacketUDP) Bytes() []byte { 328 | packBytes := new(bytes.Buffer) 329 | packBytes.Write(p.Header()) 330 | packBytes.Write(p.data) 331 | return packBytes.Bytes() 332 | } 333 | func (p *PacketUDP) Host() string { 334 | return p.dstHost 335 | } 336 | 337 | func (p *PacketUDP) Port() string { 338 | return p.dstPort 339 | } 340 | func (p *PacketUDP) Data() []byte { 341 | return p.data 342 | } 343 | -------------------------------------------------------------------------------- /lib/webtail/webtail.go: -------------------------------------------------------------------------------- 1 | package webtail 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net" 10 | "os" 11 | "strings" 12 | "time" 13 | 14 | "net/http" 15 | 16 | "github.com/alecthomas/template" 17 | "github.com/gobwas/ws" 18 | "github.com/gobwas/ws/wsutil" 19 | "github.com/julienschmidt/httprouter" 20 | ) 21 | 22 | func home(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 23 | filename := ps.ByName("name") 24 | if strings.Contains(filename, "/") || filename == "" { 25 | log.Printf("invalid log file name : %s", filename) 26 | r.Body.Close() 27 | return 28 | } 29 | homeTemplate.Execute(w, "ws://"+r.Host+"/log/"+filename) 30 | } 31 | func ViewLog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { 32 | filename := ps.ByName("name") 33 | if strings.Contains(filename, "/") || filename == "" { 34 | log.Printf("invalid log file name : %s", filename) 35 | r.Body.Close() 36 | return 37 | } 38 | logfile := fmt.Sprintf("%s/%s.log", basedir, filename) 39 | 40 | ft, err := os.Stat(logfile) 41 | if err != nil { 42 | log.Printf("can not access file %s , %s", logfile, err) 43 | r.Body.Close() 44 | return 45 | } 46 | conn, _, _, err := ws.UpgradeHTTP(r, w, nil) 47 | if err != nil { 48 | log.Printf("ws upgrade fail , %s", err) 49 | r.Body.Close() 50 | return 51 | } 52 | var ( 53 | state = ws.StateServerSide 54 | writer = wsutil.NewWriter(conn, state, ws.OpText) 55 | ) 56 | log.Printf("client online , %s", conn.RemoteAddr()) 57 | content, _ := TailN(logfile, 10) 58 | if content != "" { 59 | _, err := writer.Write([]byte(content)) 60 | if err != nil { 61 | conn.Close() 62 | log.Printf("client offline with write , %s", err) 63 | return 64 | } 65 | err = writer.Flush() 66 | if err != nil { 67 | conn.Close() 68 | log.Printf("client offline with flush , %s", err) 69 | return 70 | } 71 | } 72 | file, err := os.Open(logfile) 73 | if err != nil { 74 | conn.Close() 75 | log.Printf("open log file fail , %s", err) 76 | return 77 | } 78 | file.Seek(ft.Size(), 0) 79 | if err != nil { 80 | conn.Close() 81 | file.Close() 82 | log.Printf("sedd log file stat fail , %s", err) 83 | } 84 | reader := bufio.NewReader(file) 85 | timer := time.NewTicker(time.Millisecond * 200) 86 | go func() { 87 | defer func() { 88 | conn.Close() 89 | file.Close() 90 | timer.Stop() 91 | }() 92 | for { 93 | select { 94 | case <-timer.C: 95 | line, err := reader.ReadString('\n') 96 | if line != "" { 97 | if err != nil && err == io.EOF && !strings.Contains(line, "\n") { 98 | line += "\n" 99 | } 100 | _, err = writer.Write([]byte(line)) 101 | if err != nil { 102 | log.Printf("client offline with write, %s", err) 103 | return 104 | } 105 | err = writer.Flush() 106 | if err != nil { 107 | log.Printf("client offline with flush , %s", err) 108 | return 109 | } 110 | } 111 | } 112 | 113 | } 114 | }() 115 | go func() { 116 | _, _, err := wsutil.ReadClientData(conn) 117 | if err != nil { 118 | log.Printf("client offline with read , %s", err) 119 | conn.Close() 120 | file.Close() 121 | timer.Stop() 122 | return 123 | } 124 | }() 125 | } 126 | 127 | var ( 128 | basedir string 129 | ) 130 | 131 | func Serve(address, logDir string) (listener *net.Listener, err error) { 132 | basedir = logDir 133 | l, err := net.Listen("tcp", address) 134 | if err != nil { 135 | return 136 | } 137 | router := httprouter.New() 138 | router.GET("/log/:name", ViewLog) 139 | router.GET("/show/:name", home) 140 | log.Printf("WS Log Server on %s", l.Addr()) 141 | go func() { log.Fatal(http.Serve(l, router)) }() 142 | listener = &l 143 | return 144 | } 145 | 146 | func TailN(filename string, numLines int) (string, error) { 147 | //MAKE SURE FILENAME IS GIVEN 148 | //actually, a path to the file 149 | if numLines == 0 { 150 | numLines = 10 151 | } 152 | if len(filename) == 0 { 153 | return "", errors.New("You must provide the path to a file") 154 | } 155 | 156 | //OPEN FILE 157 | file, err := os.Open(filename) 158 | if err != nil { 159 | return "", err 160 | } 161 | defer file.Close() 162 | 163 | //SEEK BACKWARD CHARACTER BY CHARACTER ADDING UP NEW LINES 164 | //offset must start at "-1" otherwise we are already at the EOF 165 | //"-1" from numLines since we ignore "last" newline in a file 166 | numNewLines := 0 167 | var offset int64 = -1 168 | var finalReadStartPos int64 169 | for numNewLines <= numLines-1 { 170 | //seek to new position in file 171 | startPos, err := file.Seek(offset, 2) 172 | if err != nil { 173 | return "", err 174 | } 175 | 176 | //make sure start position can never be less than 0 177 | //aka, you cannot read from before the file starts 178 | if startPos == 0 { 179 | //set to -1 since we +1 to this below 180 | //the position will then start from the first character 181 | finalReadStartPos = -1 182 | break 183 | } 184 | 185 | //read the character at this position 186 | b := make([]byte, 1) 187 | _, err = file.ReadAt(b, startPos) 188 | if err != nil { 189 | return "", err 190 | } 191 | 192 | //ignore if first character being read is a newline 193 | if offset == int64(-1) && string(b) == "\n" { 194 | offset-- 195 | continue 196 | } 197 | 198 | //if the character is a newline 199 | //add this to the number of lines read 200 | //and remember position in case we have reached our target number of lines 201 | if string(b) == "\n" { 202 | numNewLines++ 203 | finalReadStartPos = startPos 204 | } 205 | 206 | //decrease offset for reading next character 207 | //remember, we are reading backward! 208 | offset-- 209 | } 210 | 211 | //READ TO END OF FILE 212 | //add "1" here to move offset from the newline position to first character in line of text 213 | //this position should be the first character in the "first" line of data we want 214 | b := make([]byte, 4096) 215 | n, err := file.ReadAt(b, finalReadStartPos+1) 216 | if n > 0 { 217 | return string(b[:n]), nil 218 | } 219 | return "", err 220 | } 221 | 222 | var homeTemplate = template.Must(template.New("").Parse(` 223 | 224 | 225 | 226 | 227 | 231 | 232 | 233 |
234 | 280 | 281 | 282 | `)) 283 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | //go:generate goversioninfo -icon=school.ico 2 | package main 3 | 4 | import ( 5 | "github.com/yincongcyincong/proxy-web/server" 6 | _ "net/http/pprof" 7 | ) 8 | 9 | 10 | func main() { 11 | server.StartServer() 12 | //clean() 13 | } 14 | 15 | 16 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf ./zip 3 | mkdir ./zip 4 | set CGO_ENABLED=0 5 | #linux 6 | GOOS=linux GOARCH=386 go build && mv proxy-web proxy/proxy-web/proxy-web && cd proxy && tar zcfv "../zip/proxy-web-linux-386.tar.gz" proxy-web && cd .. 7 | GOOS=linux GOARCH=amd64 go build && mv proxy-web proxy/proxy-web/proxy-web && cd proxy && tar zcfv "../zip/proxy-web-linux-amd64.tar.gz" proxy-web && cd .. 8 | #darwin 9 | GOOS=darwin GOARCH=386 go build go build && mv proxy-web proxy/proxy-web/proxy-web && cd proxy && tar zcfv "../zip/proxy-web-darwin-386.tar.gz" proxy-web && cd .. 10 | GOOS=darwin GOARCH=amd64 go build && mv proxy-web proxy/proxy-web/proxy-web && cd proxy && tar zcfv "../zip/proxy-web-darwin-amd64.tar.gz" proxy-web && cd .. 11 | GOOS=darwin GOARCH=arm go build && mv proxy-web proxy/proxy-web/proxy-web && cd proxy && tar zcfv "../zip/proxy-web-darwin-arm.tar.gz" proxy-web && cd .. 12 | GOOS=darwin GOARCH=arm64 go build && mv proxy-web proxy/proxy-web/proxy-web && cd proxy && tar zcfv "../zip/proxy-web-darwin-arm64.tar.gz"proxy-web && cd .. 13 | #windows 14 | cd proxy/proxy-web 15 | rm -rf proxy-web 16 | cd .. 17 | cd .. 18 | GOOS=windows GOARCH=386 go build -ldflags "-H windowsgui" && mv proxy-web.exe proxy/proxy-web/proxy-web.exe && cd proxy && tar zcfv "../zip/proxy-web-windows-386.tar.gz" proxy-web && cd .. 19 | GOOS=windows GOARCH=amd64 go build -ldflags "-H windowsgui" && mv proxy-web.exe proxy/proxy-web/proxy-web.exe && cd proxy && tar zcfv "../zip/proxy-web-windows-amd64.tar.gz" proxy-web && cd .. 20 | 21 | rm -rf proxy-web proxy-web.exe 22 | -------------------------------------------------------------------------------- /resource.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/resource.syso -------------------------------------------------------------------------------- /school.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/school.ico -------------------------------------------------------------------------------- /server/log.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "github.com/yincongcyincong/proxy-web/lib/webtail" 5 | "log" 6 | ) 7 | 8 | func InitShowLog(){ 9 | address := ":8822" 10 | basedir := "./log" 11 | listener, err := webtail.Serve(address, basedir) 12 | if err != nil { 13 | log.Fatal(err) 14 | } 15 | log.Printf("tail server on %s",(*listener).Addr()) 16 | } -------------------------------------------------------------------------------- /server/login.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "net/http" 5 | "io" 6 | "github.com/yincongcyincong/proxy-web/utils" 7 | "html/template" 8 | ) 9 | 10 | func login(v http.ResponseWriter, r *http.Request) { 11 | t, err := template.ParseFiles(dir + "/view/login.html") 12 | if err != nil { 13 | io.WriteString(v, err.Error()) 14 | return 15 | } 16 | t.Execute(v, nil) 17 | } 18 | 19 | func doLogin(v http.ResponseWriter, r *http.Request) { 20 | r.ParseForm() 21 | username, password, err := utils.NewConfig().GetUsernameAndPassword() 22 | if err != nil { 23 | v.WriteHeader(http.StatusInternalServerError) 24 | utils.ReturnJson(err.Error(), "", v) 25 | return 26 | } 27 | sess, _ := globalSessions.SessionStart(v, r) 28 | newSessionId := sess.SessionID() 29 | if lock && (sessionId != newSessionId && sessionId != "") { 30 | v.WriteHeader(http.StatusInternalServerError) 31 | utils.ReturnJson("已有人登陆", "", v) 32 | return 33 | } 34 | if (r.Form.Get("username") == username) && (r.Form.Get("password") == password) { 35 | lock = true 36 | sessionId = sess.SessionID() 37 | defer sess.SessionRelease(v) 38 | utils.ReturnJson("success", "", v) 39 | return 40 | } 41 | 42 | v.WriteHeader(http.StatusInternalServerError) 43 | utils.ReturnJson("登陆失败", "", v) 44 | } 45 | 46 | func logout(v http.ResponseWriter, r *http.Request){ 47 | r.ParseForm() 48 | logoutType := r.Form.Get("type") 49 | if logoutType == "1" { 50 | lock = false 51 | sessionId = "" 52 | } else { 53 | lock = false 54 | } 55 | 56 | utils.ReturnJson("success", "", v) 57 | } 58 | -------------------------------------------------------------------------------- /server/proxy.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | "github.com/yincongcyincong/proxy-web/utils" 6 | "html/template" 7 | "io" 8 | "io/ioutil" 9 | "net/http" 10 | "os" 11 | "os/exec" 12 | "path" 13 | "runtime" 14 | "strconv" 15 | "strings" 16 | "time" 17 | 18 | "github.com/yincongcyincong/proxy-web/lib/goproxy/sdk/android-ios" 19 | ) 20 | 21 | func add(v http.ResponseWriter, r *http.Request) { 22 | r.ParseForm() 23 | name := r.Form.Get("name") 24 | command := r.Form.Get("command") 25 | autoStart := r.Form.Get("auto") 26 | keyFile := r.Form.Get("key") 27 | crtFile := r.Form.Get("crt") 28 | log := r.Form.Get("log") 29 | 30 | serviceId, err := utils.SaveParams(name, command, autoStart, keyFile, crtFile, log) 31 | if err != nil { 32 | v.WriteHeader(http.StatusInternalServerError) 33 | utils.ReturnJson(err.Error(), "", v) 34 | return 35 | } 36 | 37 | data := make(map[string]interface{}) 38 | data["id"] = serviceId 39 | data["command"] = command 40 | data["auto_start"] = autoStart 41 | data["name"] = name 42 | data["log"] = log 43 | data["status"] = "close" 44 | utils.ReturnJson("success", data, v) 45 | } 46 | 47 | func show(w http.ResponseWriter, r *http.Request) { 48 | if r.Method == "GET" { 49 | t, err := template.ParseFiles(dir + "/view/index.html") 50 | if err != nil { 51 | io.WriteString(w, err.Error()) 52 | return 53 | } 54 | autoStart := utils.NewConfig().GetAutoStart() 55 | isProxy := utils.NewConfig().GetProxySetting() 56 | proxySetting, _ := utils.GetProxy() 57 | var ip, port string 58 | if _, ok := proxySetting["ip"]; ok { 59 | ip = proxySetting["ip"] 60 | } 61 | if _, ok := proxySetting["port"]; ok { 62 | port = proxySetting["port"] 63 | } 64 | 65 | proxyVersion := proxy.Version() 66 | data := map[string]interface{}{"auto_start": autoStart, "proxy_version": proxyVersion, "version": version, "proxy": isProxy, "ip": ip, "port": port} 67 | 68 | t.Execute(w, data) 69 | } 70 | } 71 | 72 | func getData(v http.ResponseWriter, r *http.Request) { 73 | var data interface{} 74 | var err error 75 | r.ParseForm() 76 | id := r.Form.Get("id") 77 | 78 | if id == "0" { 79 | data, err = utils.GetAllParams() 80 | } else { 81 | data, err = utils.GetParamsById(id) 82 | } 83 | if err != nil { 84 | v.WriteHeader(http.StatusInternalServerError) 85 | utils.ReturnJson(err.Error(), "", v) 86 | return 87 | } 88 | utils.ReturnJson("success", data, v) 89 | } 90 | 91 | func link(v http.ResponseWriter, r *http.Request) { 92 | if r.Method == "POST" { 93 | r.ParseForm() 94 | var command string 95 | var err error 96 | id := r.Form.Get("id") 97 | command, err = getCommand(id) 98 | if err != nil { 99 | v.WriteHeader(http.StatusInternalServerError) 100 | utils.ReturnJson(err.Error(), "", v) 101 | return 102 | } 103 | fmt.Println(command) 104 | errStr := proxy.Start(id, command) 105 | if errStr != "" { 106 | v.WriteHeader(http.StatusInternalServerError) 107 | utils.ReturnJson(errStr, "", v) 108 | return 109 | } 110 | utils.ChangeParameterDataById(id, "open") 111 | utils.ReturnJson("success", "", v) 112 | } 113 | } 114 | 115 | func getCommand(id string) (command string, err error) { 116 | parameter, err := utils.GetParamsById(id) 117 | if err != nil { 118 | return "", err 119 | } 120 | 121 | command += parameter["command"].(string) 122 | command = strings.Replace(command, "\n", " ", -1) 123 | command = strings.Replace(command, "\r", " ", -1) 124 | command = strings.Replace(command, " ", " ", -1) 125 | 126 | if parameter["key_file"].(string) != "" { 127 | command += " -K " + parameter["key_file"].(string) 128 | } 129 | if parameter["crt_file"].(string) != "" { 130 | command += " -C " + parameter["crt_file"].(string) 131 | } 132 | if parameter["log"] == "yes" { 133 | command += " --log ./log/" + parameter["id"].(string) + ".log" 134 | } 135 | s, err := os.Stat("./log/") 136 | if err != nil || !s.IsDir() { 137 | os.Mkdir("./log/", os.ModePerm) 138 | } 139 | return command, nil 140 | } 141 | 142 | func close(v http.ResponseWriter, r *http.Request) { 143 | r.ParseForm() 144 | id := r.Form.Get("id") 145 | if id == "undefined" { 146 | v.WriteHeader(http.StatusInternalServerError) 147 | utils.ReturnJson("id not found", "", v) 148 | return 149 | } 150 | err := utils.ChangeParameterDataById(id, "close") 151 | if err != nil { 152 | v.WriteHeader(http.StatusInternalServerError) 153 | utils.ReturnJson(err.Error(), "", v) 154 | return 155 | } 156 | proxy.Stop(id) 157 | utils.ReturnJson("success", "", v) 158 | return 159 | } 160 | 161 | func uploade(v http.ResponseWriter, r *http.Request) { 162 | if r.Method == "POST" { 163 | file, head, err := r.FormFile("file") 164 | fileSuffix := path.Ext(head.Filename) 165 | if err != nil { 166 | v.WriteHeader(http.StatusInternalServerError) 167 | utils.ReturnJson(err.Error(), "", v) 168 | return 169 | } 170 | defer file.Close() 171 | t := time.Now().Unix() 172 | fw, err := os.Create(dir + "/upload/" + strconv.FormatInt(t, 10) + fileSuffix) 173 | defer fw.Close() 174 | if err != nil { 175 | v.WriteHeader(http.StatusInternalServerError) 176 | utils.ReturnJson(err.Error(), "", v) 177 | return 178 | } 179 | _, err = io.Copy(fw, file) 180 | if err != nil { 181 | v.WriteHeader(http.StatusInternalServerError) 182 | utils.ReturnJson(err.Error(), "", v) 183 | return 184 | } 185 | name := fw.Name() 186 | utils.ReturnJson("", name, v) 187 | return 188 | } 189 | } 190 | 191 | func update(v http.ResponseWriter, r *http.Request) { 192 | r.ParseForm() 193 | id := r.Form.Get("id") 194 | name := r.Form.Get("name") 195 | command := r.Form.Get("command") 196 | autoStart := r.Form.Get("auto") 197 | keyFile := r.Form.Get("key") 198 | crtFile := r.Form.Get("crt") 199 | log := r.Form.Get("log") 200 | 201 | err := utils.UpdateParams(id, name, command, autoStart, keyFile, crtFile, log) 202 | if err != nil { 203 | v.WriteHeader(http.StatusInternalServerError) 204 | utils.ReturnJson(err.Error(), "", v) 205 | return 206 | } 207 | utils.ReturnJson("success", "", v) 208 | } 209 | 210 | func deleteParameter(v http.ResponseWriter, r *http.Request) { 211 | r.ParseForm() 212 | id := r.Form.Get("id") 213 | err := utils.DeleteParam(id) 214 | if err != nil { 215 | v.WriteHeader(http.StatusInternalServerError) 216 | utils.ReturnJson(err.Error(), "", v) 217 | } 218 | utils.ReturnJson("success", "", v) 219 | } 220 | 221 | func saveSetting(v http.ResponseWriter, r *http.Request) { 222 | r.ParseForm() 223 | auto := r.Form.Get("auto") 224 | proxy := r.Form.Get("proxy") 225 | ip := r.Form.Get("ip") 226 | port := r.Form.Get("port") 227 | 228 | config := utils.NewConfig() 229 | isAutoStart := config.GetAutoStart() 230 | isProxy := config.GetProxySetting() 231 | 232 | // 判断是否开启全局代理 233 | if proxy == "proxy" { 234 | if !isProxy { 235 | err := utils.StartProxy(ip, port) 236 | if err != nil { 237 | v.WriteHeader(http.StatusInternalServerError) 238 | utils.ReturnJson("修改配置失败,请使用root权限操作", err.Error(), v) 239 | return 240 | } 241 | } 242 | 243 | } else { 244 | if isProxy { 245 | utils.StopProxy(ip, port) 246 | } 247 | } 248 | 249 | switch runtime.GOOS { 250 | case "windows": 251 | 252 | if auto == "auto" { 253 | if !isAutoStart { 254 | command := dir + `/config/autostart.exe enable -k proxy-web -n proxy-web -c` 255 | commandSlice := strings.Split(command, " ") 256 | commandSlice = append(commandSlice, dir+`/proxy-web.exe c:`) 257 | cmd := exec.Command(commandSlice[0], commandSlice[1:]...) 258 | output, _ := cmd.CombinedOutput() 259 | outputStr := string(output) 260 | if !strings.Contains(outputStr, "Done") { 261 | v.WriteHeader(http.StatusInternalServerError) 262 | utils.ReturnJson("修改配置失败,请使用root权限操作", outputStr, v) 263 | return 264 | } 265 | is_success := utils.NewConfig().UpdateAutoStart("true") 266 | if !is_success { 267 | v.WriteHeader(http.StatusInternalServerError) 268 | utils.ReturnJson("修改配置失败,请使用root权限操作", "", v) 269 | return 270 | } 271 | } 272 | 273 | } else { 274 | if isAutoStart { 275 | command := dir + `/config/autostart.exe disable -k proxy-web` 276 | commandSlice := strings.Split(command, " ") 277 | cmd := exec.Command(commandSlice[0], commandSlice[1:]...) 278 | output, _ := cmd.CombinedOutput() 279 | outputStr := string(output) 280 | if !strings.Contains(outputStr, "Done") { 281 | v.WriteHeader(http.StatusInternalServerError) 282 | utils.ReturnJson("修改配置失败,请使用root权限操作", outputStr, v) 283 | return 284 | } 285 | is_success := utils.NewConfig().UpdateAutoStart("false") 286 | if !is_success { 287 | v.WriteHeader(http.StatusInternalServerError) 288 | utils.ReturnJson("修改配置失败,请使用root权限操作", "", v) 289 | return 290 | } 291 | } 292 | } 293 | 294 | case "darwin": 295 | if auto == "auto" { 296 | if !isAutoStart { 297 | command := dir + `/config/autostart enable -k proxy -n proxy -c` 298 | commandSlice := strings.Split(command, " ") 299 | commandSlice = append(commandSlice, dir+"/proxy-web") 300 | cmd := exec.Command(commandSlice[0], commandSlice[1:]...) 301 | output, err := cmd.CombinedOutput() 302 | if err != nil { 303 | v.WriteHeader(http.StatusInternalServerError) 304 | utils.ReturnJson("修改配置失败,请使用root权限操作", string(output), v) 305 | return 306 | } 307 | is_success := utils.NewConfig().UpdateAutoStart("true") 308 | if !is_success { 309 | v.WriteHeader(http.StatusInternalServerError) 310 | utils.ReturnJson("修改配置失败,请使用root权限操作", "", v) 311 | return 312 | } 313 | } 314 | } else { 315 | if isAutoStart { 316 | command := dir + `/config/autostart disable -k proxy` 317 | commandSlice := strings.Split(command, " ") 318 | cmd := exec.Command(commandSlice[0], commandSlice[1:]...) 319 | output, _ := cmd.CombinedOutput() 320 | is_success := utils.NewConfig().UpdateAutoStart("false") 321 | if !is_success { 322 | v.WriteHeader(http.StatusInternalServerError) 323 | utils.ReturnJson("修改配置失败,请使用root权限操作", string(output), v) 324 | return 325 | } 326 | } 327 | } 328 | case "linux": 329 | if auto == "auto" { 330 | if !isAutoStart { 331 | data := `#!/bin/sh 332 | ` + dir + `/proxy-web` 333 | err := ioutil.WriteFile(dir+"/config/autostart.sh", []byte(data), 0777) 334 | if err != nil { 335 | v.WriteHeader(http.StatusInternalServerError) 336 | utils.ReturnJson("修改配置失败,请使用root权限操作", "", v) 337 | return 338 | } 339 | fd, err := os.OpenFile("/etc/crontab", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777) 340 | if err != nil { 341 | v.WriteHeader(http.StatusInternalServerError) 342 | utils.ReturnJson("修改配置失败,请使用root权限操作", "", v) 343 | return 344 | } 345 | defer fd.Close() 346 | fileData, _ := ioutil.ReadAll(fd) 347 | if !strings.Contains(string(fileData), dir+"/config/autostart.sh") { 348 | fd.Write([]byte(`@reboot root ` + dir + `/config/autostart.sh 349 | `)) 350 | } 351 | 352 | is_success := utils.NewConfig().UpdateAutoStart("true") 353 | if !is_success { 354 | v.WriteHeader(http.StatusInternalServerError) 355 | utils.ReturnJson("修改配置失败,请使用root权限操作", "", v) 356 | return 357 | } 358 | } 359 | } else { 360 | if isAutoStart { 361 | os.Remove(dir + "/config/autostart.sh") 362 | is_success := utils.NewConfig().UpdateAutoStart("false") 363 | if !is_success { 364 | v.WriteHeader(http.StatusInternalServerError) 365 | utils.ReturnJson("修改配置失败,请使用root权限操作", "", v) 366 | return 367 | } 368 | } 369 | } 370 | 371 | } 372 | 373 | // 修改数据 374 | if proxy == "proxy" { 375 | if !isProxy { 376 | is_success := utils.NewConfig().UpdateProxy("true") 377 | if !is_success { 378 | v.WriteHeader(http.StatusInternalServerError) 379 | utils.ReturnJson("修改配置失败,请使用root权限操作", "", v) 380 | return 381 | } 382 | err := utils.UpdateProxy(ip, port) 383 | if err != nil { 384 | v.WriteHeader(http.StatusInternalServerError) 385 | utils.ReturnJson(err.Error(), "", v) 386 | return 387 | } 388 | } 389 | } else { 390 | if isProxy { 391 | is_success := utils.NewConfig().UpdateProxy("false") 392 | if !is_success { 393 | v.WriteHeader(http.StatusInternalServerError) 394 | utils.ReturnJson("修改配置失败,请使用root权限操作", "", v) 395 | return 396 | } 397 | } 398 | } 399 | 400 | utils.ReturnJson("success", "", v) 401 | return 402 | } 403 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "fmt" 5 | proxy "github.com/yincongcyincong/proxy-web/lib/goproxy/sdk/android-ios" 6 | "log" 7 | "net/http" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/astaxie/beego/session" 13 | "github.com/yincongcyincong/proxy-web/utils" 14 | ) 15 | 16 | var globalSessions *session.Manager 17 | var version = "v2.0" 18 | var lock = false 19 | var sessionId string 20 | var dir string 21 | 22 | func basicAuth(handler func(http.ResponseWriter, *http.Request)) http.Handler { 23 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 24 | sess, _ := globalSessions.SessionStart(w, r) 25 | newSessionId := sess.SessionID() 26 | if sessionId != newSessionId { 27 | login(w, r) 28 | return 29 | } 30 | handler(w, r) 31 | }) 32 | } 33 | 34 | func StartServer() { 35 | // 文件路径 36 | dir, _ = filepath.Abs(filepath.Dir(os.Args[0])) 37 | dir = strings.Replace(dir, "\\", "/", -1) 38 | 39 | // 启动一个websocket,判断是否有人登陆 40 | //go StartWebscoket() 41 | SetProxy() 42 | AutoStart() 43 | InitShowLog() 44 | initSession() 45 | http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir(dir+"/static")))) 46 | http.Handle("/", basicAuth(show)) 47 | http.HandleFunc("/add", add) 48 | http.HandleFunc("/update", update) 49 | http.HandleFunc("/close", close) 50 | http.HandleFunc("/link", link) 51 | http.HandleFunc("/getData", getData) 52 | http.HandleFunc("/uploade", uploade) 53 | http.HandleFunc("/delete", deleteParameter) 54 | http.HandleFunc("/saveSetting", saveSetting) 55 | http.HandleFunc("/login", login) 56 | http.HandleFunc("/doLogin", doLogin) 57 | http.HandleFunc("/logout", logout) 58 | //http.Handle("/keygen", basicAuth(keygen)) 59 | port, err := utils.NewConfig().GetServerPort() 60 | if err != nil { 61 | log.Fatal("get port failure: ", err) 62 | } 63 | fmt.Println("proxy-web: 127.0.0.1" + port) 64 | err = http.ListenAndServe(port, nil) 65 | if err != nil { 66 | log.Fatal("listen port failure", err) 67 | } 68 | } 69 | 70 | func AutoStart() { 71 | datas, err := utils.InitParams() 72 | if err != nil { 73 | return 74 | } 75 | for _, data := range datas { 76 | var command string 77 | command += data["command"].(string) 78 | command = strings.Replace(command, "\n", "", -1) 79 | command = strings.Replace(command, "\r", "", -1) 80 | command = strings.Replace(command, " ", " ", -1) 81 | if data["key_file"].(string) != "" { 82 | command += " -K " + data["key_file"].(string) 83 | } 84 | if data["crt_file"].(string) != "" { 85 | command += " -C " + data["crt_file"].(string) 86 | } 87 | if data["log"] == "yes" { 88 | command += " --log " + dir + "/log/" + data["id"].(string) + ".log" 89 | } 90 | s, err := os.Stat(dir + "/log/") 91 | if err != nil || !s.IsDir() { 92 | os.Mkdir(dir+"/log/", os.ModePerm) 93 | } 94 | go autoRunCommand(data["id"].(string), command) 95 | } 96 | } 97 | 98 | func autoRunCommand(id, command string) { 99 | fmt.Println(command) 100 | errStr := proxy.Start(id, command) 101 | if errStr != "" { 102 | utils.ChangeParameterDataById(id, "close") 103 | } 104 | } 105 | 106 | func initSession() { 107 | sessionConfig := &session.ManagerConfig{ 108 | CookieName: "sessionid", 109 | EnableSetCookie: true, 110 | Gclifetime: 360000, 111 | Maxlifetime: 360000, 112 | Secure: false, 113 | CookieLifeTime: 360000, 114 | ProviderConfig: dir + "/tmp", 115 | } 116 | globalSessions, _ = session.NewManager("file", sessionConfig) 117 | go globalSessions.GC() 118 | } 119 | 120 | func SetProxy() { 121 | data, err := utils.GetProxy() 122 | if err != nil { 123 | return 124 | } 125 | proxy := utils.NewConfig().GetProxySetting() 126 | if !proxy { 127 | return 128 | } 129 | utils.StartProxy(data["ip"], data["port"]) 130 | } 131 | 132 | //func StartWebscoket() { 133 | // http.Handle("/websocket", websocket.Handler(svrConnHandler)) 134 | // log.Fatal(http.ListenAndServe(":8222", nil)) 135 | //} 136 | // 137 | //func svrConnHandler(conn *websocket.Conn) { 138 | // request := make([]byte, 128) 139 | // defer conn.Close() 140 | // readLen, err := conn.Read(request) 141 | // if err != nil { 142 | // return 143 | // } 144 | // 145 | // if string(request[:readLen]) == "close" { 146 | // lock = false 147 | // } else { 148 | // lock = true 149 | // } 150 | // 151 | //} 152 | -------------------------------------------------------------------------------- /static/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/.DS_Store -------------------------------------------------------------------------------- /static/css/price.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background-image: url(../images/NX-Desktop-BG.png); 3 | } 4 | 5 | #pricing-table { 6 | margin: 100px auto; 7 | text-align: center; 8 | width: 0px; /* total computed width = 222 x 3 + 226 */ 9 | } 10 | 11 | #pricing-table .plan { 12 | font: 12px 'Lucida Sans', 'trebuchet MS', Arial, Helvetica; 13 | text-shadow: 0 1px rgba(255,255,255,.8); 14 | background: #fff; 15 | border: 1px solid #ddd; 16 | color: #333; 17 | padding: 20px; 18 | width: 200px; /* plan width = 180 + 20 + 20 + 1 + 1 = 222px */ 19 | float: left; 20 | position: relative; 21 | } 22 | 23 | #pricing-table #most-popular { 24 | z-index: 2; 25 | top: -13px; 26 | border-width: 3px; 27 | padding: 30px 20px; 28 | -moz-border-radius: 5px; 29 | -webkit-border-radius: 5px; 30 | border-radius: 5px; 31 | -moz-box-shadow: 20px 0 10px -10px rgba(0, 0, 0, .15), -20px 0 10px -10px rgba(0, 0, 0, .15); 32 | -webkit-box-shadow: 20px 0 10px -10px rgba(0, 0, 0, .15), -20px 0 10px -10px rgba(0, 0, 0, .15); 33 | box-shadow: 20px 0 10px -10px rgba(0, 0, 0, .15), -20px 0 10px -10px rgba(0, 0, 0, .15); 34 | } 35 | 36 | #pricing-table .plan:nth-child(1) { 37 | -moz-border-radius: 5px 0 0 5px; 38 | -webkit-border-radius: 5px 0 0 5px; 39 | border-radius: 5px 0 0 5px; 40 | } 41 | 42 | #pricing-table .plan:nth-child(4) { 43 | -moz-border-radius: 0 5px 5px 0; 44 | -webkit-border-radius: 0 5px 5px 0; 45 | border-radius: 0 5px 5px 0; 46 | } 47 | 48 | /* --------------- */ 49 | 50 | #pricing-table h3 { 51 | font-size: 20px; 52 | font-weight: normal; 53 | padding: 20px; 54 | margin: -20px -20px 50px -20px; 55 | background-color: #eee; 56 | background-image: -moz-linear-gradient(#000,#74ebe6); 57 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#eee)); 58 | background-image: -webkit-linear-gradient(#fff, #eee); 59 | background-image: -o-linear-gradient(#fff, #eee); 60 | background-image: -ms-linear-gradient(#fff, #eee); 61 | background-image: linear-gradient(#000,#74ebe6); 62 | } 63 | 64 | #pricing-table #most-popular h3 { 65 | background-color: #ddd; 66 | background-image: -moz-linear-gradient(#eee,#ddd); 67 | background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#ddd)); 68 | background-image: -webkit-linear-gradient(#eee, #ddd); 69 | background-image: -o-linear-gradient(#eee, #ddd); 70 | background-image: -ms-linear-gradient(#eee, #ddd); 71 | background-image: linear-gradient(#000,#74ebe6); 72 | margin-top: -30px; 73 | padding-top: 30px; 74 | -moz-border-radius: 5px 5px 0 0; 75 | -webkit-border-radius: 5px 5px 0 0; 76 | border-radius: 5px 5px 0 0; 77 | } 78 | 79 | #pricing-table .plan:nth-child(1) h3 { 80 | -moz-border-radius: 5px 0 0 0; 81 | -webkit-border-radius: 5px 0 0 0; 82 | border-radius: 5px 0 0 0; 83 | } 84 | 85 | #pricing-table .plan:nth-child(4) h3 { 86 | -moz-border-radius: 0 5px 0 0; 87 | -webkit-border-radius: 0 5px 0 0; 88 | border-radius: 0 5px 0 0; 89 | } 90 | 91 | #pricing-table h3 span { 92 | display: block; 93 | cursor:pointer; 94 | font: bold 25px/100px Georgia, Serif; 95 | color: #777; 96 | background: #fff; 97 | border: 5px solid #fff; 98 | height: 100px; 99 | width: 100px; 100 | margin: 10px auto -65px; 101 | -moz-border-radius: 100px; 102 | -webkit-border-radius: 100px; 103 | border-radius: 100px; 104 | -moz-box-shadow: 0 5px 20px #ddd inset, 0 3px 0 #999 inset; 105 | -webkit-box-shadow: 0 5px 20px #ddd inset, 0 3px 0 #999 inset; 106 | box-shadow: 0 5px 20px #ddd inset, 0 3px 0 #999 inset; 107 | } 108 | 109 | /* --------------- */ 110 | 111 | #pricing-table ul { 112 | margin: 20px 0 0 0; 113 | padding: 0; 114 | list-style: none; 115 | } 116 | 117 | #pricing-table li { 118 | border-top: 1px solid #ddd; 119 | padding: 10px 0; 120 | } 121 | 122 | /* --------------- */ 123 | 124 | .signup { 125 | position: relative; 126 | padding: 8px 13px; 127 | margin: 20px 0 10px 0; 128 | color: #74ebe6 !important; 129 | text-transform: uppercase; 130 | text-decoration: none !important; 131 | display: inline-block; 132 | cursor:pointer; 133 | background-color: #000; 134 | background-image: -moz-linear-gradient(#72ce3f, #62bc30); 135 | background-image: -webkit-gradient(linear, left top, left bottom, from(#72ce3f), to(#62bc30)); 136 | background-image: -webkit-linear-gradient(#72ce3f, #62bc30); 137 | background-image: -o-linear-gradient(#72ce3f, #62bc30); 138 | background-image: -ms-linear-gradient(#72ce3f, #62bc30); 139 | background-image: linear-gradient(#000, #000); 140 | -moz-border-radius: 3px; 141 | -webkit-border-radius: 3px; 142 | border-radius: 3px; 143 | text-shadow: 0 1px 0 rgba(0,0,0,.3); 144 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 2px 0 rgba(0, 0, 0, .7); 145 | -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 2px 0 rgba(0, 0, 0, .7); 146 | box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 2px 0 rgba(0, 0, 0, .7); 147 | } 148 | 149 | 150 | .signupNoMargin { 151 | position: relative; 152 | padding: 8px 20px; 153 | color: #74ebe6; 154 | text-transform: uppercase; 155 | text-decoration: none; 156 | display: inline-block; 157 | cursor:pointer; 158 | background-color: #000; 159 | background-image: -moz-linear-gradient(#72ce3f, #62bc30); 160 | background-image: -webkit-gradient(linear, left top, left bottom, from(#72ce3f), to(#62bc30)); 161 | background-image: -webkit-linear-gradient(#72ce3f, #62bc30); 162 | background-image: -o-linear-gradient(#72ce3f, #62bc30); 163 | background-image: -ms-linear-gradient(#72ce3f, #62bc30); 164 | background-image: linear-gradient(#000, #000); 165 | -moz-border-radius: 3px; 166 | -webkit-border-radius: 3px; 167 | border-radius: 3px; 168 | text-shadow: 0 1px 0 rgba(0,0,0,.3); 169 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 2px 0 rgba(0, 0, 0, .7); 170 | -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 2px 0 rgba(0, 0, 0, .7); 171 | box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 2px 0 rgba(0, 0, 0, .7); 172 | } 173 | 174 | .signupNoMargin:hover{ 175 | color: #74ebe6; 176 | text-decoration : none; 177 | cursor:pointer; 178 | } 179 | 180 | .modal-header{ 181 | background-image: linear-gradient(#000,#74ebe6); 182 | } 183 | 184 | #exampleModalLabel{ 185 | color:#fff; 186 | } 187 | 188 | .modal-header span{ 189 | color:#fff; 190 | } 191 | 192 | .signupNoMarginPadding { 193 | position: relative; 194 | color: #74ebe6; 195 | text-transform: uppercase; 196 | text-decoration: none; 197 | display: inline-block; 198 | cursor:pointer; 199 | background-color: #000; 200 | background-image: -moz-linear-gradient(#72ce3f, #62bc30); 201 | background-image: -webkit-gradient(linear, left top, left bottom, from(#72ce3f), to(#62bc30)); 202 | background-image: -webkit-linear-gradient(#72ce3f, #62bc30); 203 | background-image: -o-linear-gradient(#72ce3f, #62bc30); 204 | background-image: -ms-linear-gradient(#72ce3f, #62bc30); 205 | background-image: linear-gradient(#000, #000); 206 | -moz-border-radius: 3px; 207 | -webkit-border-radius: 3px; 208 | border-radius: 3px; 209 | text-shadow: 0 1px 0 rgba(0,0,0,.3); 210 | -moz-box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 2px 0 rgba(0, 0, 0, .7); 211 | -webkit-box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 2px 0 rgba(0, 0, 0, .7); 212 | box-shadow: 0 1px 0 rgba(255, 255, 255, .5), 0 2px 0 rgba(0, 0, 0, .7); 213 | } 214 | 215 | .signupNoMarginPadding:hover{ 216 | color: #74ebe6; 217 | text-decoration : none; 218 | cursor:pointer; 219 | } 220 | 221 | #pricing-table .signup:active, #pricing-table .signup:focus { 222 | background: #000; 223 | top: 2px; 224 | -moz-box-shadow: 0 0 3px rgba(0, 0, 0, .7) inset; 225 | -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, .7) inset; 226 | box-shadow: 0 0 3px rgba(0, 0, 0, .7) inset; 227 | } 228 | 229 | /* --------------- */ 230 | 231 | .clear:before, .clear:after { 232 | content:""; 233 | display:table 234 | } 235 | 236 | .clear:after { 237 | clear:both 238 | } 239 | 240 | .clear { 241 | zoom:1 242 | } 243 | 244 | .plan span{ 245 | word-wrap:break-word; 246 | } 247 | 248 | .commandSpan { 249 | height: 50px; 250 | } 251 | -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /static/images/NX-Desktop-BG.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/images/NX-Desktop-BG.png -------------------------------------------------------------------------------- /static/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /static/js/theme/default/icon-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/js/theme/default/icon-ext.png -------------------------------------------------------------------------------- /static/js/theme/default/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/js/theme/default/icon.png -------------------------------------------------------------------------------- /static/js/theme/default/loading-0.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/js/theme/default/loading-0.gif -------------------------------------------------------------------------------- /static/js/theme/default/loading-1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/js/theme/default/loading-1.gif -------------------------------------------------------------------------------- /static/js/theme/default/loading-2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/js/theme/default/loading-2.gif -------------------------------------------------------------------------------- /static/webupload/Uploader.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yincongcyincong/proxy-web/c4a955db0958186ea1253697411db4eee32e7605/static/webupload/Uploader.swf -------------------------------------------------------------------------------- /static/webupload/webuploader.css: -------------------------------------------------------------------------------- 1 | .webuploader-container { 2 | position: relative; 3 | } 4 | .webuploader-element-invisible { 5 | position: absolute !important; 6 | clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ 7 | clip: rect(1px,1px,1px,1px); 8 | } 9 | .webuploader-pick { 10 | position: relative; 11 | display: inline-block; 12 | cursor: pointer; 13 | background: #00b7ee; 14 | padding: 10px 15px; 15 | color: #fff; 16 | text-align: center; 17 | border-radius: 3px; 18 | overflow: hidden; 19 | } 20 | .webuploader-pick-hover { 21 | background: #00a2d4; 22 | } 23 | 24 | .webuploader-pick-disable { 25 | opacity: 0.6; 26 | pointer-events:none; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /utils/always.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func AlwaysCommand(always string, level int) string { 4 | if always == "1" && level == 2 { 5 | return " --always" 6 | } 7 | return "" 8 | } 9 | -------------------------------------------------------------------------------- /utils/compress.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | func CompressCommand(compress string) string { 4 | if compress == "1" { 5 | return " --c" 6 | } 7 | return "" 8 | } 9 | -------------------------------------------------------------------------------- /utils/config.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "log" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/Unknwon/goconfig" 10 | ) 11 | 12 | type Config struct { 13 | File *goconfig.ConfigFile 14 | } 15 | 16 | func NewConfig() *Config { 17 | dir, _ := filepath.Abs(filepath.Dir(os.Args[0])) 18 | dir = strings.Replace(dir, "\\", "/", -1) 19 | config, err := goconfig.LoadConfigFile(dir + "/config/config.ini") 20 | if err != nil { 21 | log.Fatal(err.Error()) 22 | } 23 | return &Config{ 24 | File: config, 25 | } 26 | } 27 | 28 | func (c *Config) GetServerPath() (string, error) { 29 | path, err := c.File.GetValue("proxy_server", "path") 30 | if err != nil { 31 | return "", err 32 | } 33 | return path, nil 34 | } 35 | 36 | func (c *Config) GetServerPort() (string, error) { 37 | path, err := c.File.GetValue("proxy_server", "port") 38 | if err != nil { 39 | return "", err 40 | } 41 | return path, nil 42 | } 43 | 44 | func (c *Config) GetUsernameAndPassword() (string, string, error) { 45 | username, err := c.File.GetValue("proxy_server", "username") 46 | if err != nil { 47 | return "", "", err 48 | } 49 | password, err := c.File.GetValue("proxy_server", "password") 50 | if err != nil { 51 | return "", "", err 52 | } 53 | return username, password, nil 54 | } 55 | 56 | func (c *Config) GetServicesFilePath() (string, error) { 57 | serviceFile, err := c.File.GetValue("proxy_server", "services") 58 | if err != nil { 59 | return "", err 60 | } 61 | return dir + serviceFile, nil 62 | } 63 | 64 | func (c *Config) UpdateAutoStart(autoStart string) (isSuccess bool) { 65 | c.File.DeleteKey("config", "auto_start") 66 | isSuccess = c.File.SetValue("config", "auto_start", autoStart) 67 | goconfig.SaveConfigFile(c.File, dir+"/config/config.ini") 68 | return 69 | } 70 | 71 | func (c *Config) UpdateProxy(proxy string) (isSuccess bool) { 72 | c.File.DeleteKey("config", "proxy") 73 | isSuccess = c.File.SetValue("config", "proxy", proxy) 74 | goconfig.SaveConfigFile(c.File, dir+"/config/config.ini") 75 | return 76 | } 77 | 78 | func (c *Config) GetAutoStart() (autoStart bool) { 79 | autoStart = c.File.MustBool("config", "auto_start") 80 | return 81 | } 82 | 83 | func (c *Config) GetProxySetting() (proxy bool) { 84 | proxy = c.File.MustBool("config", "proxy") 85 | return 86 | } 87 | -------------------------------------------------------------------------------- /utils/convert.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "strconv" 5 | "reflect" 6 | "fmt" 7 | ) 8 | 9 | type Convert struct{} 10 | 11 | func NewConvert() *Convert { 12 | return &Convert{} 13 | } 14 | 15 | // bool 转化为字符串 16 | func (convert *Convert) BoolToString(boolValue bool) string { 17 | if boolValue == true { 18 | return "true" 19 | } else { 20 | return "false" 21 | } 22 | } 23 | 24 | //bool 转化为 int 25 | func (convert *Convert) BoolToInt(boolValue bool) int { 26 | if boolValue == true { 27 | return 1 28 | } else { 29 | return 0 30 | } 31 | } 32 | 33 | //int 转化为 bool 34 | func (convert *Convert) IntToBool(number int) bool { 35 | if number == 0 { 36 | return false 37 | } else { 38 | return true 39 | } 40 | } 41 | 42 | //int 转化为字符串 43 | //base 范围 2-32 进制 44 | func (convert *Convert) IntToString(number int64, base int) string { 45 | return strconv.FormatInt(number, base) 46 | } 47 | 48 | //string to int(10进制) 49 | func (convert *Convert) StringToInt(str string) int { 50 | intValue, _ := strconv.Atoi(str) 51 | return intValue 52 | } 53 | 54 | // string to int64(10进制) 55 | func (convert *Convert) StringToInt64(str string) int64 { 56 | intValue, _ := strconv.ParseInt(str, 10, 64) 57 | return intValue 58 | } 59 | 60 | // int 转化为10进制字符串 IntToString(number, 10) 61 | func (convert *Convert) IntToTenString(number int) string { 62 | return strconv.Itoa(number) 63 | } 64 | 65 | // float 转化为字符串 66 | func (convert *Convert) FloatToString(f float64, fmt byte, prec, bitSize int) string { 67 | return strconv.FormatFloat(f, fmt, prec, bitSize) 68 | } 69 | 70 | // 转化任何的数为 int64 71 | func (convert *Convert) ToInt64(value interface{}) (d int64, err error) { 72 | val := reflect.ValueOf(value) 73 | switch value.(type) { 74 | case int, int8, int16, int32, int64: 75 | d = val.Int() 76 | case uint, uint8, uint16, uint32, uint64: 77 | d = int64(val.Uint()) 78 | default: 79 | err = fmt.Errorf("ToInt64 need numeric not `%T`", value) 80 | } 81 | return 82 | } -------------------------------------------------------------------------------- /utils/data.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "os" 7 | "os/exec" 8 | "path/filepath" 9 | "runtime" 10 | "strings" 11 | "time" 12 | 13 | "github.com/pkg/errors" 14 | ) 15 | 16 | var dataFilePath string 17 | var dir string 18 | 19 | func init() { 20 | dir, _ = filepath.Abs(filepath.Dir(os.Args[0])) 21 | dir = strings.Replace(dir, "\\", "/", -1) 22 | dataFilePath = dir + "/data/services/" 23 | } 24 | 25 | func SaveParams(name, command, auto_start, key_file, crt_file, log string) (serviceIdStr string, err error) { 26 | serviceId := time.Now().UnixNano() / 1000000 27 | serviceIdStr = NewConvert().IntToString(serviceId, 10) 28 | filePath, err := NewConfig().GetServicesFilePath() 29 | if err != nil { 30 | return 31 | } 32 | fd, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644) 33 | if err != nil { 34 | return 35 | } 36 | data, err := ioutil.ReadAll(fd) 37 | if err != nil { 38 | return 39 | } 40 | fd.Close() 41 | dataMap := make(map[string]interface{}) 42 | json.Unmarshal(data, &dataMap) 43 | dataMap[serviceIdStr] = auto_start 44 | data, _ = json.Marshal(dataMap) 45 | ioutil.WriteFile(filePath, data, 0644) 46 | 47 | // 判断有没有services文件夹 48 | s, err := os.Stat(dataFilePath) 49 | if err != nil || !s.IsDir() { 50 | os.Mkdir(dataFilePath, os.ModePerm) 51 | } 52 | 53 | fd, err = os.OpenFile(dataFilePath+serviceIdStr+".json", os.O_RDWR|os.O_CREATE, 0644) 54 | if err != nil { 55 | return 56 | } 57 | params := make(map[string]string) 58 | params["name"] = name 59 | params["command"] = command 60 | params["auto_start"] = auto_start 61 | params["key_file"] = key_file 62 | params["crt_file"] = crt_file 63 | params["id"] = serviceIdStr 64 | params["status"] = "close" 65 | params["log"] = log 66 | paramJson, _ := json.Marshal(params) 67 | fd.Write(paramJson) 68 | fd.Close() 69 | return 70 | } 71 | 72 | func UpdateParams(serviceId, name, command, auto_start, key_file, crt_file, log string) (err error) { 73 | filePath, err := NewConfig().GetServicesFilePath() 74 | if err != nil { 75 | return 76 | } 77 | fd, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644) 78 | if err != nil { 79 | return 80 | } 81 | data, err := ioutil.ReadAll(fd) 82 | if err != nil { 83 | return 84 | } 85 | fd.Close() 86 | dataMap := make(map[string]interface{}) 87 | json.Unmarshal(data, &dataMap) 88 | dataMap[serviceId] = auto_start 89 | data, _ = json.Marshal(dataMap) 90 | ioutil.WriteFile(filePath, data, 0644) 91 | 92 | // 判断有没有services文件夹 93 | s, err := os.Stat(dataFilePath) 94 | if err != nil || !s.IsDir() { 95 | os.Mkdir(dataFilePath, os.ModePerm) 96 | } 97 | 98 | fd, err = os.OpenFile(dataFilePath+serviceId+".json", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) 99 | if err != nil { 100 | return 101 | } 102 | params := make(map[string]string) 103 | params["name"] = name 104 | params["command"] = command 105 | params["auto_start"] = auto_start 106 | params["key_file"] = key_file 107 | params["crt_file"] = crt_file 108 | params["id"] = serviceId 109 | params["status"] = "close" 110 | params["log"] = log 111 | paramJson, _ := json.Marshal(params) 112 | fd.Write(paramJson) 113 | fd.Close() 114 | return 115 | } 116 | 117 | func DeleteParam(serviceId string) (err error) { 118 | filePath, err := NewConfig().GetServicesFilePath() 119 | if err != nil { 120 | return 121 | } 122 | fd, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644) 123 | if err != nil { 124 | return 125 | } 126 | defer fd.Close() 127 | 128 | allData, err := ioutil.ReadAll(fd) 129 | if err != nil { 130 | return 131 | } 132 | dataMap := make(map[string]interface{}) 133 | err = json.Unmarshal(allData, &dataMap) 134 | if err != nil { 135 | return 136 | } 137 | delete(dataMap, serviceId) 138 | dataByte, _ := json.Marshal(dataMap) 139 | ioutil.WriteFile(filePath, dataByte, 0644) 140 | os.Remove(dataFilePath + serviceId + ".json") 141 | return 142 | } 143 | 144 | func InitParams() (datas []map[string]interface{}, err error) { 145 | filePath, err := NewConfig().GetServicesFilePath() 146 | fd, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644) 147 | if err != nil { 148 | return 149 | } 150 | defer fd.Close() 151 | 152 | allData, err := ioutil.ReadAll(fd) 153 | if err != nil { 154 | return 155 | } 156 | dataMap := make(map[string]interface{}) 157 | err = json.Unmarshal(allData, &dataMap) 158 | if err != nil { 159 | return 160 | } 161 | 162 | for serviceId, auto_start := range dataMap { 163 | data := make(map[string]interface{}) 164 | fd1, err := os.OpenFile(dataFilePath+serviceId+".json", os.O_RDWR|os.O_CREATE, 0644) 165 | if err != nil { 166 | continue 167 | } 168 | dataByte, err := ioutil.ReadAll(fd1) 169 | if err != nil { 170 | continue 171 | } 172 | json.Unmarshal(dataByte, &data) 173 | 174 | if auto_start == "yes" { 175 | data["status"] = "open" 176 | datas = append(datas, data) 177 | } else { 178 | data["status"] = "close" 179 | } 180 | 181 | dataByte, _ = json.Marshal(data) 182 | ioutil.WriteFile(dataFilePath+serviceId+".json", dataByte, 0644) 183 | fd1.Close() 184 | } 185 | 186 | return 187 | } 188 | 189 | func GetAllParams() (datas []map[string]interface{}, err error) { 190 | datas = make([]map[string]interface{}, 0) 191 | filePath, err := NewConfig().GetServicesFilePath() 192 | fd, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644) 193 | if err != nil { 194 | return 195 | } 196 | defer fd.Close() 197 | 198 | allData, err := ioutil.ReadAll(fd) 199 | if err != nil || len(allData) == 0 { 200 | return 201 | } 202 | dataMap := make(map[string]interface{}) 203 | err = json.Unmarshal(allData, &dataMap) 204 | if err != nil { 205 | return 206 | } 207 | 208 | var closeData []map[string]interface{} 209 | for serviceId, _ := range dataMap { 210 | data := make(map[string]interface{}) 211 | fd, err := os.Open(dataFilePath + serviceId + ".json") 212 | if err != nil { 213 | continue 214 | } 215 | dataByte, err := ioutil.ReadAll(fd) 216 | if err != nil { 217 | continue 218 | } 219 | json.Unmarshal(dataByte, &data) 220 | if data["status"] == "open" { 221 | datas = append(datas, data) 222 | } else { 223 | closeData = append(closeData, data) 224 | } 225 | 226 | fd.Close() 227 | } 228 | 229 | datas = append(datas, closeData...) 230 | 231 | return 232 | } 233 | 234 | func GetParamsById(id string) (data map[string]interface{}, err error) { 235 | fd, err := os.OpenFile(dataFilePath+id+".json", os.O_RDWR|os.O_CREATE, 0644) 236 | if err != nil { 237 | return 238 | } 239 | defer fd.Close() 240 | 241 | allData, err := ioutil.ReadAll(fd) 242 | if err != nil { 243 | return 244 | } 245 | err = json.Unmarshal(allData, &data) 246 | 247 | return 248 | } 249 | 250 | func ChangeParameterDataById(serviceId, status string) (err error) { 251 | fd, err := os.OpenFile(dataFilePath+serviceId+".json", os.O_RDWR|os.O_CREATE, 0644) 252 | if err != nil { 253 | return 254 | } 255 | defer fd.Close() 256 | data, err := ioutil.ReadAll(fd) 257 | if err != nil { 258 | return 259 | } 260 | params := make(map[string]string) 261 | err = json.Unmarshal(data, ¶ms) 262 | if err != nil { 263 | return 264 | } 265 | params["status"] = status 266 | paramJson, _ := json.Marshal(params) 267 | ioutil.WriteFile(dataFilePath+serviceId+".json", paramJson, 0644) 268 | return 269 | } 270 | 271 | func UpdateProxy(ip, port string) (err error) { 272 | data := make(map[string]interface{}) 273 | data["ip"] = ip 274 | data["port"] = port 275 | dataByte, err := json.Marshal(data) 276 | if err != nil { 277 | return 278 | } 279 | 280 | err = ioutil.WriteFile(dir+"/data/proxy.json", dataByte, 0644) 281 | return 282 | } 283 | 284 | func GetProxy() (data map[string]string, err error) { 285 | dataByte, err := ioutil.ReadFile(dir + "/data/proxy.json") 286 | if err != nil { 287 | return 288 | } 289 | 290 | err = json.Unmarshal(dataByte, &data) 291 | return 292 | } 293 | 294 | func StartProxy(ip, port string) (err error) { 295 | switch runtime.GOOS { 296 | case "windows": 297 | addr := ip + ":" + port 298 | command := dir + "/config/proxysetting.exe http=" + addr + " https=" + addr 299 | commandSlice := strings.Split(command, " ") 300 | cmd := exec.Command(commandSlice[0], commandSlice[1:]...) 301 | output, _ := cmd.CombinedOutput() 302 | outputStr := string(output) 303 | if outputStr != "" { 304 | return errors.New(outputStr) 305 | } 306 | 307 | case "darwin": 308 | cmd := exec.Command("/bin/bash", "-c", dir+"/config/httpProxy.sh "+ip+" "+port) 309 | output, _ := cmd.CombinedOutput() 310 | outputStr := string(output) 311 | if !strings.Contains(outputStr, "successfully") { 312 | return errors.New(outputStr) 313 | } 314 | case "linux": 315 | addr := ip + ":" + port 316 | home := os.Getenv("HOME") 317 | var contentByte []byte 318 | contentByte, _ = ioutil.ReadFile(home + "/.bashrc") 319 | if err != nil { 320 | return 321 | } 322 | content := string(contentByte) 323 | if !strings.Contains(content, dir+"/config/linux_proxy.sh") { 324 | content = content + ` 325 | . ` + dir + `/config/linux_proxy.sh` 326 | err = ioutil.WriteFile(home+"/.bashrc", []byte(content), 0777) 327 | if err != nil { 328 | return err 329 | } 330 | } 331 | shContent := `#!/bin/sh 332 | export http_proxy=` + addr + ` 333 | export https_proxy=` + addr 334 | err = ioutil.WriteFile(dir+"/config/linux_proxy.sh", []byte(shContent), 0777) 335 | if err != nil { 336 | return err 337 | } 338 | } 339 | return 340 | } 341 | 342 | func StopProxy(ip, port string) (err error) { 343 | switch runtime.GOOS { 344 | case "windows": 345 | command := dir + "/config/proxysetting.exe stop" 346 | commandSlice := strings.Split(command, " ") 347 | cmd := exec.Command(commandSlice[0], commandSlice[1:]...) 348 | output, _ := cmd.CombinedOutput() 349 | outputStr := string(output) 350 | if outputStr != "" { 351 | return errors.New(outputStr) 352 | } 353 | case "darwin": 354 | cmd := exec.Command("/bin/bash", "-c", dir+"/config/httpProxy.sh "+ip+" "+port+" close") 355 | output, _ := cmd.CombinedOutput() 356 | outputStr := string(output) 357 | if !strings.Contains(outputStr, "successfully") { 358 | return errors.New(outputStr) 359 | } 360 | case "linux": 361 | os.Remove(dir + "/config/linux_proxy.sh") 362 | } 363 | 364 | return 365 | } 366 | -------------------------------------------------------------------------------- /utils/error.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "net/http" 6 | ) 7 | 8 | type Result struct { 9 | Msg string `json:"msg"` 10 | Data interface{} `json:"data"` 11 | } 12 | 13 | func ReturnJson(msg string, data interface{}, v http.ResponseWriter) { 14 | r := Result{Msg: msg, Data: data} 15 | json.NewEncoder(v).Encode(r) 16 | } 17 | -------------------------------------------------------------------------------- /versioninfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "FixedFileInfo": { 3 | "FileVersion": { 4 | "Major": 2, 5 | "Minor": 0, 6 | "Patch": 0, 7 | "Build": 0 8 | }, 9 | "ProductVersion": { 10 | "Major": 2, 11 | "Minor": 0, 12 | "Patch": 0, 13 | "Build": 0 14 | }, 15 | "FileFlagsMask": "3f", 16 | "FileFlags ": "00", 17 | "FileOS": "040004", 18 | "FileType": "01", 19 | "FileSubType": "00" 20 | }, 21 | "StringFileInfo": { 22 | "Comments": "proxy-web", 23 | "CompanyName": "yc", 24 | "FileDescription": "proxy-web启动器", 25 | "FileVersion": "2.0", 26 | "InternalName": "proxy-web.exe", 27 | "LegalCopyright": "648588267@qq.com", 28 | "LegalTrademarks": "", 29 | "OriginalFilename": "proxy-web.exe", 30 | "PrivateBuild": "", 31 | "ProductName": "proxy-web", 32 | "ProductVersion": "v2.0", 33 | "SpecialBuild": "" 34 | }, 35 | "VarFileInfo": { 36 | "Translation": { 37 | "LangID": "0409", 38 | "CharsetID": "04B0" 39 | } 40 | } 41 | } 42 | --------------------------------------------------------------------------------