diff --git a/CHANGELOG b/CHANGELOG index a241863..2eb1f0e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,12 @@ proxy更新日志 +v5.3 +1.优化了socks_client握手端口判断,避免了sstap测试UDP失败的问题. + +v5.2 +1.修复了HTTP(S)\SPS反向代理无法正常工作的问题. +2.优化了智能判断,减少不必要的DNS解析. +3.重构了SOCKS和SPS的UDP功能,基于UDP的游戏加速嗖嗖的. + v5.1 1.优化了kcp默认mtu配置,调整为450. 2.优化了HTTP(S)\SOCKS5代理智能判断,更加精确。 diff --git a/Dockerfile b/Dockerfile index 91863f3..e2a5099 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,82 @@ -FROM golang:1.10.3-alpine as builder +FROM alpine:3.7 AS builder + +RUN apk add --no-cache \ + ca-certificates + +# set up nsswitch.conf for Go's "netgo" implementation +# - https://github.com/golang/go/blob/go1.9.1/src/net/conf.go#L194-L275 +# - docker run --rm debian:stretch grep '^hosts:' /etc/nsswitch.conf +RUN [ ! -e /etc/nsswitch.conf ] && echo 'hosts: files dns' > /etc/nsswitch.conf + +ENV GOLANG_VERSION 1.10.3 + +# make-sure-R0-is-zero-before-main-on-ppc64le.patch: https://github.com/golang/go/commit/9aea0e89b6df032c29d0add8d69ba2c95f1106d9 (Go 1.9) +#COPY *.patch /go-alpine-patches/ + +RUN set -eux; \ + apk add --no-cache --virtual .build-deps \ + bash \ + gcc \ + musl-dev \ + openssl \ + go \ + ; \ + export \ +# set GOROOT_BOOTSTRAP such that we can actually build Go + GOROOT_BOOTSTRAP="$(go env GOROOT)" \ +# ... and set "cross-building" related vars to the installed system's values so that we create a build targeting the proper arch +# (for example, if our build host is GOARCH=amd64, but our build env/image is GOARCH=386, our build needs GOARCH=386) + GOOS="$(go env GOOS)" \ + GOARCH="$(go env GOARCH)" \ + GOHOSTOS="$(go env GOHOSTOS)" \ + GOHOSTARCH="$(go env GOHOSTARCH)" \ + ; \ +# also explicitly set GO386 and GOARM if appropriate +# https://github.com/docker-library/golang/issues/184 + apkArch="$(apk --print-arch)"; \ + case "$apkArch" in \ + armhf) export GOARM='6' ;; \ + x86) export GO386='387' ;; \ + esac; \ + \ + wget -O go.tgz "https://golang.org/dl/go$GOLANG_VERSION.src.tar.gz"; \ + echo '567b1cc66c9704d1c019c50bef946272e911ec6baf244310f87f4e678be155f2 *go.tgz' | sha256sum -c -; \ + tar -C /usr/local -xzf go.tgz; \ + rm go.tgz; \ + \ + cd /usr/local/go/src; \ + for p in /go-alpine-patches/*.patch; do \ + [ -f "$p" ] || continue; \ + patch -p2 -i "$p"; \ + done; \ + ./make.bash; \ + \ + rm -rf /go-alpine-patches; \ + apk del .build-deps; \ + \ + export PATH="/usr/local/go/bin:$PATH"; \ + go version + +ENV GOPATH /go +ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH + +RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH" +WORKDIR $GOPATH + ARG GOPROXY_VERSION=master -RUN apk update && apk upgrade && \ - apk add --no-cache git && cd /go/src/ && git clone https://github.com/snail007/goproxy && \ - cd goproxy && git checkout ${GOPROXY_VERSION} && \ - go get && CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o proxy -FROM alpine:3.7 -COPY --from=builder /go/src/goproxy/proxy / -CMD /proxy ${OPTS} +RUN apk update; apk upgrade; \ + apk add --no-cache git; \ + cd /go/src/; \ + mkdir github.com; \ + mkdir github.com/snail007; \ + cd github.com/snail007; \ + git clone https://github.com/snail007/goproxy.git; \ + cd goproxy; \ + git checkout ${GOPROXY_VERSION}; \ + CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -a -installsuffix cgo -o proxy; \ + chmod 0777 proxy + +FROM 1.10.3-stretch +RUN mkdir /proxy && chmod 0777 /proxy +COPY --from=builder builder /go/src/github.com/snail007/goproxy/proxy /proxy/ +CMD cd /proxy && /proxy ${OPTS} \ No newline at end of file diff --git a/README.md b/README.md index 110a8cd..96ef72d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,18 @@ Proxy is a high performance HTTP, HTTPS, HTTPS, websocket, TCP, UDP, Socks5 prox [![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy/) [![license](https://img.shields.io/github/license/snail007/goproxy.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy/total.svg?style=plastic)](https://github.com/snail007/goproxy/releases) [![download](https://img.shields.io/github/release/snail007/goproxy.svg?style=plastic)](https://github.com/snail007/goproxy/releases) -[中文手册](/README_ZH.md) **[全平台GUI版本](/gui/README.md)** **[全平台SDK](/sdk/README.md)** +**[中文手册](/README_ZH.md)** + +**[全平台图形界面版本](/gui/README.md)** + +**[全平台SDK](/sdk/README.md)** + +### How to contribute to the code (Pull Request)? + +Pull Request is welcomed. +First, you need to clone the project to your account, and then modify the code on the dev branch. +Finally, Pull Request to dev branch of goproxy project, and contribute code for efficiency. +PR needs to explain what changes have been made and why you change them. ### Features - chain-style proxy: the program itself can be a primary proxy, and if a parent proxy is set, it can be used as a second level proxy or even a N level proxy. @@ -37,26 +48,7 @@ Proxy is a high performance HTTP, HTTPS, HTTPS, websocket, TCP, UDP, Socks5 prox - ...   -This page is the v5.0 manual, and the other version of the manual can be checked by the following link. -- [v4.9 manual](https://github.com/snail007/goproxy/tree/v4.9) -- [v4.8 manual](https://github.com/snail007/goproxy/tree/v4.8) -- [v4.7 manual](https://github.com/snail007/goproxy/tree/v4.7) -- [v4.6 manual](https://github.com/snail007/goproxy/tree/v4.6) -- [v4.5 manual](https://github.com/snail007/goproxy/tree/v4.5) -- [v4.4 manual](https://github.com/snail007/goproxy/tree/v4.4) -- [v4.3 manual](https://github.com/snail007/goproxy/tree/v4.3) -- [v4.2 manual](https://github.com/snail007/goproxy/tree/v4.2) -- [v4.0-4.1 manual](https://github.com/snail007/goproxy/tree/v4.1) -- [v3.9 manual](https://github.com/snail007/goproxy/tree/v3.9) -- [v3.8 manual](https://github.com/snail007/goproxy/tree/v3.8) -- [v3.6-v3.7 manual](https://github.com/snail007/goproxy/tree/v3.6) -- [v3.5 manual](https://github.com/snail007/goproxy/tree/v3.5) -- [v3.4 manual](https://github.com/snail007/goproxy/tree/v3.4) -- [v3.3 manual](https://github.com/snail007/goproxy/tree/v3.3) -- [v3.2 manual](https://github.com/snail007/goproxy/tree/v3.2) -- [v3.1 manual](https://github.com/snail007/goproxy/tree/v3.1) -- [v3.0 manual](https://github.com/snail007/goproxy/tree/v3.0) -- [v2.x manual](https://github.com/snail007/goproxy/tree/v2.2) +This page is the v5.3 manual, and the other version of the manual can be checked by the following [link](docs/old-release.md). ### How to find the organization? [Click to join the proxy group of gitter](https://gitter.im/go-proxy/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link)   @@ -169,7 +161,7 @@ If the installation fails or your VPS is not a linux64 system, please follow the Download address: https://github.com/snail007/goproxy/releases ```shell cd /root/proxy/ -wget https://github.com/snail007/goproxy/releases/download/v5.0/proxy-linux-amd64.tar.gz +wget https://github.com/snail007/goproxy/releases/download/v5.3/proxy-linux-amd64.tar.gz ``` #### **2.Download the automatic installation script** ```shell @@ -181,12 +173,12 @@ chmod +x install.sh #### Docker installation -Dockerfile root of project uses multistage build and alpine project to comply with best practices. Uses golang 1.10.3 for building as noted in the project README.md and will be pretty small image. total extracted size will be 17.3MB for goproxy version 5.0. +Dockerfile root of project uses multistage build and alpine project to comply with best practices. Uses golang 1.10.3 for building as noted in the project README.md and will be pretty small image. total extracted size will be 17.3MB for goproxy latest version. -The default build process builds the master branch (latest commits/ cutting edge), and it can be configured to build specific version, just edit Dockerfile before build, following builds release version 4.7: +The default build process builds the master branch (latest commits/ cutting edge), and it can be configured to build specific version, just edit Dockerfile before build, following builds release version 5.3: ``` -ARG GOPROXY_VERSION=v5.0 +ARG GOPROXY_VERSION=v5.3 ``` To Run: @@ -196,12 +188,12 @@ sudo docker build . ``` 2. Tag the image: ``` -sudo docker tag goproxy/goproxy:latest +sudo docker tag snail007/goproxy:latest ``` 3. Run! Just put your arguments to proxy binary in the OPTS environmental variable (this is just a sample http proxy): ``` -sudo docker run -d --restart=always --name goproxy -e OPTS="http -p :33080" -p 33080:33080 goproxy/goproxy:latest +sudo docker run -d --restart=always --name goproxy -e OPTS="http -p :33080" -p 33080:33080 snail007/goproxy:latest ``` 4. View logs: ``` @@ -235,9 +227,18 @@ for example, --log proxy.log, The log will be exported to proxy.log file which i ### **Generating a communication certificate file** HTTP, TCP, UDP proxy process will communicate with parent proxy. In order to secure, we use encrypted communication. Of course, we can choose not to encrypted communication. All communication with parent proxy in this tutorial is encrypted, requiring certificate files. -The OpenSSL command is installed on the Linux and encrypted certificate can be generated directly through the following command. -`./proxy keygen` -By default, the certificate file proxy.crt and the key file proxy.key are generated under the current program directory. + +1.Generate signed certificates and key files through the following commands. +`./proxy keygen -C proxy` +The certificate file proxy.crt and key file proxy.key will be generated under the current directory. + +2.Through the following commands, use the signed certificate proxy.crt and key file proxy.key to issue new certificates: goproxy.crt and goproxy.key. +`./proxy keygen -s -C proxy -c goproxy` +The certificate file goproxy.crt and key file goproxy.key will be generated under the current program directory. + +3.By default, the domain name in the certificate is a random domain and can be specified using the `-n test.com` parameter. + +4.More usage:`proxy keygen --help`。 ### **Daemon mode** After the default execution of proxy, if you want to keep proxy running, you can't close the command line. @@ -831,7 +832,7 @@ through this way, When you visits the website by local proxy 8080, it visits the ### **6.Proxy protocol conversion** #### **6.1.Functional introduction** -The proxy protocol conversion use the SPS subcommand (abbreviation of socks+https), SPS itself does not provide the proxy function, just accept the proxy request and then converse protocol and forwarded to the existing HTTP (s) or Socks5 proxy. SPS can use existing HTTP (s) or Socks5 proxy converse to support HTTP (s) and Socks5 HTTP (s) proxy at the same time by one port, and proxy supports forward and reverse proxy (SNI), SOCKS5 proxy which is conversed does not support UDP. in addition to the existing HTTP or Socks5 proxy, which supports TLS, TCP, KCP three modes and chain-style connection. That is more than one SPS node connection can build encryption channel. +The proxy protocol conversion use the SPS subcommand (abbreviation of socks+https), SPS itself does not provide the proxy function, just accept the proxy request and then converse protocol and forwarded to the existing HTTP (s) or Socks5 proxy. SPS can use existing HTTP (s) or Socks5 proxy converse to support HTTP (s) and Socks5 HTTP (s) proxy at the same time by one port, and proxy supports forward and reverse proxy (SNI), SOCKS5 proxy which is also does support UDP when parent is Socks5. in addition to the existing HTTP or Socks5 proxy, which supports TLS, TCP, KCP three modes and chain-style connection. That is more than one SPS node connection can build encryption channel. #### **6.2.HTTP(S) to HTTP(S) + SOCKS5** Suppose there is a common HTTP (s) proxy: 127.0.0.1:8080. Now we turn it into a common proxy that supports HTTP (s) and Socks5 at the same time. The local port after transformation is 18080. @@ -1073,19 +1074,15 @@ Then the local UDP port 53 provides a security and anti pollution DNS analysis. - HTTP (s) proxy support PAC? - Welcome joining group feedback... -### How to contribute to the code (Pull Request)? -First, you need to clone the project to your account, and then modify the code on the dev branch. -Finally, Pull Request to dev branch of goproxy project, and contribute code for efficiency. -PR needs to explain what changes have been made and why you change them. +### How to use the source code? -### How to use the source code? Recommend go1.10.1. `go get github.com/snail007/goproxy` use command cd to enter your go SRC directory then cd to enter `github.com/snail007/goproxy`. Direct compilation:`go build -o proxy` execution: `go run *.go` -`utils` is a toolkit, and `service` is a specific service class. +`utils` is a toolkit, and `service` is a specific service class. ### License Proxy is licensed under GPLv3 license. diff --git a/README_ZH.md b/README_ZH.md index 07b2ebc..fd831d4 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -7,7 +7,17 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 [![stable](https://img.shields.io/badge/stable-stable-green.svg)](https://github.com/snail007/goproxy/) [![license](https://img.shields.io/github/license/snail007/goproxy.svg?style=plastic)]() [![download_count](https://img.shields.io/github/downloads/snail007/goproxy/total.svg?style=plastic)](https://github.com/snail007/goproxy/releases) [![download](https://img.shields.io/github/release/snail007/goproxy.svg?style=plastic)](https://github.com/snail007/goproxy/releases) -**[English Manual](/README.md)** **[全平台GUI版本](/gui/README.md)** **[全平台SDK](/sdk/README.md)** +**[English Manual](/README.md)** + +**[全平台图形界面版本](/gui/README.md)** + +**[全平台SDK](/sdk/README.md)** + +### 如何贡献代码(Pull Request)? + +欢迎加入一起发展壮大proxy.首先需要clone本项目到自己的帐号下面, +然后在dev分支上面修改代码,最后发Pull Request到goproxy项目的dev分支即可, +为了高效贡献代码,pr的时候需要说明做了什么变更,原因是什么. ### Features - 链式代理,程序本身可以作为一级代理,如果设置了上级代理那么可以作为二级代理,乃至N级代理. @@ -38,7 +48,7 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp,socks5代理服务 - ... -本页是v5.1手册,其他版本手册请点击[这里](docs/old-release.md)查看. +本页是v5.3手册,其他版本手册请点击[这里](docs/old-release.md)查看. ### 怎么找到组织? @@ -149,7 +159,7 @@ curl -L https://raw.githubusercontent.com/snail007/goproxy/master/install_auto.s 下载地址:https://github.com/snail007/goproxy/releases ```shell cd /root/proxy/ -wget https://github.com/snail007/goproxy/releases/download/v5.1/proxy-linux-amd64.tar.gz +wget https://github.com/snail007/goproxy/releases/download/v5.3/proxy-linux-amd64.tar.gz ``` #### **2.下载自动安装脚本** ```shell @@ -160,12 +170,12 @@ chmod +x install.sh ``` #### Docker安装 -项目根目录的Dockerfile文件用来构建,使用golang 1.10.3,构建基于goproxy v5.1, -全部大小17.3MB,默认情况下使用master分支,不过可以通过修改配置文件Dockerfile -或者使用参数GOPROXY_VERSION指定构建的goproxy版本. +项目根目录的Dockerfile文件用来构建,使用golang 1.10.3,构建基于goproxy的master分支最新版本, +全部大小17.3MB,默认情况下使用master分支,不过可以通过修改配置文件Dockerfile +或者使用参数GOPROXY_VERSION指定构建的goproxy版本. ``` -ARG GOPROXY_VERSION=v5.1 +ARG GOPROXY_VERSION=v5.3 ``` 步骤: @@ -175,14 +185,14 @@ sudo docker build . ``` 2. 镜像打标签: ``` -sudo docker tag <上一步的结果ID> goproxy/goproxy:latest +sudo docker tag <上一步的结果ID> snail007/goproxy:latest ``` 3. 运行 参数OPTS的值就是传递给proxy的所有参数 -比如下面的例子启动了一个http服务: +比如下面的例子启动了一个http服务: ``` -sudo docker run -d --restart=always --name goproxy -e OPTS="http -p :33080" -p 33080:33080 goproxy/goproxy:latest +sudo docker run -d --restart=always --name goproxy -e OPTS="http -p :33080" -p 33080:33080 snail007/goproxy:latest ``` 4. 查看日志: ``` @@ -689,7 +699,14 @@ server连接到bridge的时候,如果同时有多个client连接到同一个brid `./proxy help client` ### **5.SOCKS5代理** -提示:SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证. +提示: + +SOCKS5代理,支持CONNECT,UDP协议,不支持BIND,支持用户名密码认证. + +***如果你的VPS是阿里云,腾讯云这种VPS,就是ifconfig看不见你的公网IP,只能看见内网IP,*** + +***那么需要加上`-g VPS公网IP`参数,SOCKS5代理的UDP功能才能正常工作。*** + #### **5.1.普通SOCKS5代理** `./proxy socks -t tcp -p "0.0.0.0:38080"` @@ -837,7 +854,7 @@ proxy的socks代理在tcp之上可以通过自定义加密和tls标准加密以 ### **6.代理协议转换** #### **6.1 功能介绍** -代理协议转换使用的是sps子命令(socks+https的缩写),sps本身不提供代理功能,只是接受代理请求"转换并转发"给已经存在的http(s)代理或者socks5代理;sps可以把已经存在的http(s)代理或者socks5代理转换为一个端口同时支持http(s)和socks5代理,而且http(s)代理支持正向代理和反向代理(SNI),转换后的SOCKS5代理不支持UDP功能;另外对于已经存在的http(s)代理或者socks5代理,支持tls、tcp、kcp三种模式,支持链式连接,也就是可以多个sps结点层级连接构建加密通道。 +代理协议转换使用的是sps子命令(socks+https的缩写),sps本身不提供代理功能,只是接受代理请求"转换并转发"给已经存在的http(s)代理或者socks5代理;sps可以把已经存在的http(s)代理或者socks5代理转换为一个端口同时支持http(s)和socks5代理,而且http(s)代理支持正向代理和反向代理(SNI),转换后的SOCKS5代理,当上级是SOCKS5时仍然支持UDP功能;另外对于已经存在的http(s)代理或者socks5代理,支持tls、tcp、kcp三种模式,支持链式连接,也就是可以多个sps结点层级连接构建加密通道。 #### **6.2 HTTP(S)转HTTP(S)+SOCKS5** 假设已经存在一个普通的http(s)代理:127.0.0.1:8080,现在我们把它转为同时支持http(s)和socks5的普通代理,转换后的本地端口为18080。 @@ -1109,11 +1126,6 @@ fast3:`--nodelay=1 --interval=10 --resend=2 --nc=1` - http(s)代理增加pac支持? - 欢迎加群反馈... -### 如何贡献代码(Pull Request)? -首先需要clone本项目到自己的帐号下面,然后在dev分支上面修改代码, -最后发Pull Request到goproxy项目的dev分支即可,为了高效贡献代码, -pr的时候需要说明做了什么变更,原因是什么. - ### 如何使用源码? 建议go1.10.1. `go get github.com/snail007/goproxy` @@ -1133,5 +1145,3 @@ QQ交流群:189618940 如果proxy帮助你解决了很多问题,你可以通过下面的捐赠更好的支持proxy. - - diff --git a/config.go b/config.go index 7b633db..4aeeb9a 100755 --- a/config.go +++ b/config.go @@ -198,8 +198,6 @@ func initConfig() (err error) { socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type ").Default("tcp").Short('T').Enum("tls", "tcp", "kcp", "ssh") socksArgs.LocalType = socks.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() - socksArgs.UDPParent = socks.Flag("udp-parent", "udp parent address, such as: \"23.32.32.19:33090\"").Default("").Short('X').String() - socksArgs.UDPLocal = socks.Flag("udp-local", "udp local ip:port to listen").Short('x').Default(":33090").String() socksArgs.CertFile = socks.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() socksArgs.CaCertFile = socks.Flag("ca", "ca cert file for tls").Default("").String() socksArgs.KeyFile = socks.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() @@ -388,6 +386,7 @@ func initConfig() (err error) { for { if cmd != nil { cmd.Process.Kill() + time.Sleep(time.Second * 5) } cmd = exec.Command(os.Args[0], args...) cmdReaderStderr, err := cmd.StderrPipe() @@ -423,7 +422,6 @@ func initConfig() (err error) { continue } log.Printf("worker %s [PID] %d unexpected exited, restarting...\n", os.Args[0], pid) - time.Sleep(time.Second * 5) } }() return diff --git a/docs/old-release.md b/docs/old-release.md index aba1110..d7c3c40 100644 --- a/docs/old-release.md +++ b/docs/old-release.md @@ -1,5 +1,7 @@ # Old Versions of Proxy +- [v5.2手册](https://github.com/snail007/goproxy/tree/v5.2) +- [v5.1手册](https://github.com/snail007/goproxy/tree/v5.1) - [v5.0手册](https://github.com/snail007/goproxy/tree/v5.0) - [v4.9手册](https://github.com/snail007/goproxy/tree/v4.9) - [v4.8手册](https://github.com/snail007/goproxy/tree/v4.8) diff --git a/install_auto.sh b/install_auto.sh index 6f1723f..515a2a7 100755 --- a/install_auto.sh +++ b/install_auto.sh @@ -5,7 +5,7 @@ if [ -e /tmp/proxy ]; then fi mkdir /tmp/proxy cd /tmp/proxy -wget https://github.com/snail007/goproxy/releases/download/v5.1/proxy-linux-amd64.tar.gz +wget https://github.com/snail007/goproxy/releases/download/v5.3/proxy-linux-amd64.tar.gz # #install proxy tar zxvf proxy-linux-amd64.tar.gz diff --git a/keygen.sh b/keygen.sh deleted file mode 100755 index 69a4f80..0000000 --- a/keygen.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -openssl genrsa -out proxy.key 2048 -openssl req -new -key proxy.key -x509 -days 3650 -out proxy.crt -subj /C=CN/ST=BJ/O="Localhost Ltd"/CN=proxy \ No newline at end of file diff --git a/main.go b/main.go index 66239c7..900bab4 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,7 @@ import ( "github.com/snail007/goproxy/services" ) -const APP_VERSION = "5.1" +const APP_VERSION = "5.3" func main() { err := initConfig() diff --git a/release.sh b/release.sh index 2c505c5..98508d6 100755 --- a/release.sh +++ b/release.sh @@ -1,5 +1,5 @@ #!/bin/bash -VER="5.1" +VER="5.3" RELEASE="release-${VER}" rm -rf .cert mkdir .cert diff --git a/sdk/CHANGELOG b/sdk/CHANGELOG index e2d614a..fd37799 100644 --- a/sdk/CHANGELOG +++ b/sdk/CHANGELOG @@ -1,5 +1,13 @@ SDK更新日志 +v5.3 +1.增加了支持日志输出回调的方法: + StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback) +2.优化了socks_client握手端口判断,避免了sstap测试UDP失败的问题.. +3.修复了HTTP(S)\SPS反向代理无法正常工作的问题. +4.优化了智能判断,减少不必要的DNS解析. +5.重构了SOCKS和SPS的UDP功能,基于UDP的游戏加速嗖嗖的. + v4.9 1.修复了HTTP Basic代理返回不合适的头部,导致浏览器不会弹框,个别代理插件无法认证的问题. 2.内网穿透切换smux到yamux. diff --git a/sdk/README.md b/sdk/README.md index d65c405..f21503e 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -25,7 +25,7 @@ proxy使用gombile实现了一份go代码编译为android和ios平台下面可 #### 1.导入包 ```java -import snail007.proxy.Porxy +import snail007.proxy.Proxy ``` #### 2.启动一个服务 diff --git a/sdk/android-ios/dns.go b/sdk/android-ios/dns.go index bb2dc4a..375016d 100644 --- a/sdk/android-ios/dns.go +++ b/sdk/android-ios/dns.go @@ -76,7 +76,7 @@ func (s *DNS) InitService() (err error) { nil, &net.Dialer{ Timeout: 5 * time.Second, - KeepAlive: 5 * time.Second, + KeepAlive: 2 * time.Second, }, ) if err != nil { @@ -117,7 +117,7 @@ func (s *DNS) StopService() { if e != nil { s.log.Printf("stop dns service crashed,%s", e) } else { - s.log.Printf("service dns stoped") + s.log.Printf("service dns stopped") } }() Stop(s.serviceKey) diff --git a/sdk/android-ios/release_android.sh b/sdk/android-ios/release_android.sh index d591df8..a52adfa 100755 --- a/sdk/android-ios/release_android.sh +++ b/sdk/android-ios/release_android.sh @@ -1,5 +1,5 @@ #/bin/bash -VER="v5.0" +VER="v5.3" rm -rf sdk-android-*.tar.gz rm -rf android mkdir android diff --git a/sdk/android-ios/release_ios.sh b/sdk/android-ios/release_ios.sh index d7b6e00..633981c 100755 --- a/sdk/android-ios/release_ios.sh +++ b/sdk/android-ios/release_ios.sh @@ -1,5 +1,5 @@ #/bin/bash -VER="v5.0" +VER="v5.3" rm -rf sdk-ios-*.tar.gz rm -rf ios mkdir ios diff --git a/sdk/android-ios/sdk.go b/sdk/android-ios/sdk.go index 1033d62..55873e7 100644 --- a/sdk/android-ios/sdk.go +++ b/sdk/android-ios/sdk.go @@ -23,12 +23,31 @@ import ( kingpin "gopkg.in/alecthomas/kingpin.v2" ) -const SDK_VERSION = "5.0" +const SDK_VERSION = "5.3" var ( app *kingpin.Application ) +type LogCallback interface { + Write(line string) +} +type logCallback interface { + Write(line string) +} +type logWriter struct { + callback LogCallback +} + +func (s *logWriter) Write(p []byte) (n int, err error) { + s.callback.Write(string(p)) + return +} + +func Start(serviceID, serviceArgsStr string) (errStr string) { + return StartWithLog(serviceID, serviceArgsStr, nil) +} + //Start //serviceID : is service identify id,different service's id should be difference //serviceArgsStr: is the whole command line args string @@ -38,7 +57,7 @@ var ( //and so on. //if an error occured , errStr will be the error reason //if start success, errStr is empty. -func Start(serviceID, serviceArgsStr string) (errStr string) { +func StartWithLog(serviceID, serviceArgsStr string, loggerCallback LogCallback) (errStr string) { //define args tcpArgs := tcpx.TCPArgs{} httpArgs := httpx.HTTPArgs{} @@ -194,8 +213,6 @@ func Start(serviceID, serviceArgsStr string) (errStr string) { socksArgs.ParentType = socks.Flag("parent-type", "parent protocol type ").Default("tcp").Short('T').Enum("tls", "tcp", "kcp", "ssh") socksArgs.LocalType = socks.Flag("local-type", "local protocol type ").Default("tcp").Short('t').Enum("tls", "tcp", "kcp") socksArgs.Local = socks.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String() - socksArgs.UDPParent = socks.Flag("udp-parent", "udp parent address, such as: \"23.32.32.19:33090\"").Default("").Short('X').String() - socksArgs.UDPLocal = socks.Flag("udp-local", "udp local ip:port to listen").Short('x').Default(":33090").String() socksArgs.CertFile = socks.Flag("cert", "cert file for tls").Short('C').Default("proxy.crt").String() socksArgs.CaCertFile = socks.Flag("ca", "ca cert file for tls").Default("").String() socksArgs.KeyFile = socks.Flag("key", "key file for tls").Short('K').Default("proxy.key").String() @@ -339,12 +356,18 @@ func Start(serviceID, serviceArgsStr string) (errStr string) { } log.SetFlags(flags) - if *logfile != "" { - f, e := os.OpenFile(*logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) - if e != nil { - log.Fatal(e) + if loggerCallback == nil { + if *logfile != "" { + f, e := os.OpenFile(*logfile, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) + if e != nil { + log.Fatal(e) + } + log.SetOutput(f) } - log.SetOutput(f) + } else { + log.SetOutput(&logWriter{ + callback: loggerCallback, + }) } //regist services and run service diff --git a/sdk/windows-linux/release_linux.sh b/sdk/windows-linux/release_linux.sh index de15e3d..76aaf4b 100755 --- a/sdk/windows-linux/release_linux.sh +++ b/sdk/windows-linux/release_linux.sh @@ -1,5 +1,5 @@ #/bin/bash -VER="v5.0" +VER="v5.3" rm -rf sdk-linux-*.tar.gz rm -rf README.md libproxy-sdk.so libproxy-sdk.h libproxy-sdk.a diff --git a/sdk/windows-linux/release_mac.sh b/sdk/windows-linux/release_mac.sh index afa9a02..b2b5e4e 100755 --- a/sdk/windows-linux/release_mac.sh +++ b/sdk/windows-linux/release_mac.sh @@ -1,5 +1,5 @@ #/bin/bash -VER="v5.0" +VER="v5.3" rm -rf *.tar.gz rm -rf README.md libproxy-sdk.dylib libproxy-sdk.h diff --git a/sdk/windows-linux/release_windows.sh b/sdk/windows-linux/release_windows.sh index 7a3b06d..ab26512 100755 --- a/sdk/windows-linux/release_windows.sh +++ b/sdk/windows-linux/release_windows.sh @@ -1,8 +1,8 @@ #/bin/bash -VER="v5.0" +VER="v5.3" -sudo rm /usr/local/go -sudo ln -s /usr/local/go1.10.1 /usr/local/go +#sudo rm /usr/local/go +#sudo ln -s /usr/local/go1.10.1 /usr/local/go rm -rf sdk-windows-*.tar.gz rm -rf README.md proxy-sdk.h proxy-sdk.dll @@ -22,7 +22,7 @@ cp ../README.md . tar zcf sdk-windows-32bit-${VER}.tar.gz README.md proxy-sdk.dll proxy-sdk.h ieshims.dll rm -rf README.md proxy-sdk.h proxy-sdk.dll -sudo rm /usr/local/go -sudo ln -s /usr/local/go1.8.5 /usr/local/go +#sudo rm /usr/local/go +#sudo ln -s /usr/local/go1.8.5 /usr/local/go echo "done." diff --git a/services/http/http.go b/services/http/http.go index 16d41a3..0d33093 100644 --- a/services/http/http.go +++ b/services/http/http.go @@ -185,11 +185,13 @@ func (s *HTTP) StopService() { if e != nil { s.log.Printf("stop http(s) service crashed,%s", e) } else { - s.log.Printf("service http(s) stoped") + s.log.Printf("service http(s) stopped") } }() s.isStop = true - s.checker.Stop() + if *s.cfg.Parent != "" { + s.checker.Stop() + } if s.sshClient != nil { s.sshClient.Close() } @@ -277,10 +279,11 @@ func (s *HTTP) callback(inConn net.Conn) { } else if *s.cfg.Always { useProxy = true } else { - k := s.Resolve(address) - s.checker.Add(address, k) - //var n, m uint - useProxy, _, _ = s.checker.IsBlocked(k) + var isInMap bool + useProxy, isInMap, _, _ = s.checker.IsBlocked(address) + if !isInMap { + s.checker.Add(address, s.Resolve(address)) + } //s.log.Printf("blocked ? : %v, %s , fail:%d ,success:%d", useProxy, address, n, m) } } @@ -344,7 +347,6 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut Password: *s.cfg.ParentKey, }) } - outAddr := outConn.RemoteAddr().String() //outLocalAddr := outConn.LocalAddr().String() if req.IsHTTPS() && (!useProxy || *s.cfg.ParentType == "ssh") { @@ -353,8 +355,8 @@ func (s *HTTP) OutToTCP(useProxy bool, address string, inConn *net.Conn, req *ut } else { //https或者http,上级是代理,proxy需要转发 outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - //直连目标或上级非代理,清理HTTP头部的代理头信息 - if !useProxy || *s.cfg.ParentType == "ssh" { + //直连目标或上级非代理或非SNI,清理HTTP头部的代理头信息. + if (!useProxy || *s.cfg.ParentType == "ssh") && !req.IsSNI { _, err = outConn.Write(utils.RemoveProxyHeaders(req.HeadBuf)) } else { _, err = outConn.Write(req.HeadBuf) @@ -492,7 +494,7 @@ func (s *HTTP) IsDeadLoop(inLocalAddr string, host string) bool { if *s.cfg.DNSAddress != "" { outIPs = []net.IP{net.ParseIP(s.Resolve(outDomain))} } else { - outIPs, err = net.LookupIP(outDomain) + outIPs, err = utils.MyLookupIP(outDomain) } if err == nil { for _, ip := range outIPs { @@ -524,6 +526,7 @@ func (s *HTTP) Resolve(address string) string { ip, err := s.domainResolver.Resolve(address) if err != nil { s.log.Printf("dns error %s , ERR:%s", address, err) + return address } return ip } diff --git a/services/mux/mux_bridge.go b/services/mux/mux_bridge.go index e80b7c5..5268151 100644 --- a/services/mux/mux_bridge.go +++ b/services/mux/mux_bridge.go @@ -80,7 +80,7 @@ func (s *MuxBridge) StopService() { if e != nil { s.log.Printf("stop bridge service crashed,%s", e) } else { - s.log.Printf("service bridge stoped") + s.log.Printf("service bridge stopped") } }() s.isStop = true @@ -88,7 +88,7 @@ func (s *MuxBridge) StopService() { (*(*s.sc).Listener).Close() } for _, g := range s.clientControlConns.Items() { - for _, session := range g.(utils.ConcurrentMap).Items() { + for _, session := range g.(*utils.ConcurrentMap).Items() { (session.(*smux.Session)).Close() } } diff --git a/services/mux/mux_client.go b/services/mux/mux_client.go index b5c263a..3b55819 100644 --- a/services/mux/mux_client.go +++ b/services/mux/mux_client.go @@ -73,7 +73,7 @@ func (s *MuxClient) StopService() { if e != nil { s.log.Printf("stop client service crashed,%s", e) } else { - s.log.Printf("service client stoped") + s.log.Printf("service client stopped") } }() s.isStop = true diff --git a/services/mux/mux_server.go b/services/mux/mux_server.go index 5f34715..d6ebef8 100644 --- a/services/mux/mux_server.go +++ b/services/mux/mux_server.go @@ -182,7 +182,7 @@ func (s *MuxServer) StopService() { if e != nil { s.log.Printf("stop server service crashed,%s", e) } else { - s.log.Printf("service server stoped") + s.log.Printf("service server stopped") } }() s.isStop = true diff --git a/services/socks/socks.go b/services/socks/socks.go index b740966..b7856f2 100644 --- a/services/socks/socks.go +++ b/services/socks/socks.go @@ -3,6 +3,7 @@ package socks import ( "crypto/tls" "fmt" + "io" "io/ioutil" logger "log" "net" @@ -13,7 +14,6 @@ import ( "github.com/snail007/goproxy/services" "github.com/snail007/goproxy/services/kcpcfg" "github.com/snail007/goproxy/utils" - "github.com/snail007/goproxy/utils/aes" "github.com/snail007/goproxy/utils/conncrypt" "github.com/snail007/goproxy/utils/socks" "golang.org/x/crypto/ssh" @@ -48,8 +48,6 @@ type SocksArgs struct { AuthURLTimeout *int AuthURLRetry *int KCP kcpcfg.KCPConfigArgs - UDPParent *string - UDPLocal *string LocalIPS *[]string DNSAddress *string DNSTTL *int @@ -59,27 +57,31 @@ type SocksArgs struct { ParentCompress *bool } type Socks struct { - cfg SocksArgs - checker utils.Checker - basicAuth utils.BasicAuth - sshClient *ssh.Client - lockChn chan bool - udpSC utils.ServerChannel - sc *utils.ServerChannel - domainResolver utils.DomainResolver - isStop bool - userConns utils.ConcurrentMap - log *logger.Logger + cfg SocksArgs + checker utils.Checker + basicAuth utils.BasicAuth + sshClient *ssh.Client + lockChn chan bool + udpSC utils.ServerChannel + sc *utils.ServerChannel + domainResolver utils.DomainResolver + isStop bool + userConns utils.ConcurrentMap + log *logger.Logger + udpRelatedPacketConns utils.ConcurrentMap + udpLocalKey []byte + udpParentKey []byte } func NewSocks() services.Service { return &Socks{ - cfg: SocksArgs{}, - checker: utils.Checker{}, - basicAuth: utils.BasicAuth{}, - lockChn: make(chan bool, 1), - isStop: false, - userConns: utils.NewConcurrentMap(), + cfg: SocksArgs{}, + checker: utils.Checker{}, + basicAuth: utils.BasicAuth{}, + lockChn: make(chan bool, 1), + isStop: false, + userConns: utils.NewConcurrentMap(), + udpRelatedPacketConns: utils.NewConcurrentMap(), } } @@ -103,17 +105,6 @@ func (s *Socks) CheckArgs() (err error) { err = fmt.Errorf("parent type unkown,use -T ") return } - host, _, e := net.SplitHostPort(*s.cfg.Parent) - if e != nil { - err = fmt.Errorf("parent format error : %s", e) - return - } - if *s.cfg.UDPParent == "" { - *s.cfg.UDPParent = net.JoinHostPort(host, "33090") - } - if strings.HasPrefix(*s.cfg.UDPParent, ":") { - *s.cfg.UDPParent = net.JoinHostPort(host, strings.TrimLeft(*s.cfg.UDPParent, ":")) - } if *s.cfg.ParentType == "ssh" { if *s.cfg.SSHUser == "" { err = fmt.Errorf("ssh user required") @@ -145,6 +136,9 @@ func (s *Socks) CheckArgs() (err error) { } } } + s.udpLocalKey = s.LocalUDPKey() + s.udpParentKey = s.ParentUDPKey() + //s.log.Printf("udpLocalKey : %v , udpParentKey : %v", s.udpLocalKey, s.udpParentKey) return } func (s *Socks) InitService() (err error) { @@ -152,7 +146,9 @@ func (s *Socks) InitService() (err error) { if *s.cfg.DNSAddress != "" { (*s).domainResolver = utils.NewDomainResolver(*s.cfg.DNSAddress, *s.cfg.DNSTTL, s.log) } - s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct, s.log) + if *s.cfg.Parent != "" { + s.checker = utils.NewChecker(*s.cfg.Timeout, int64(*s.cfg.Interval), *s.cfg.Blocked, *s.cfg.Direct, s.log) + } if *s.cfg.ParentType == "ssh" { e := s.ConnectSSH() if e != nil { @@ -186,14 +182,6 @@ func (s *Socks) InitService() (err error) { } if *s.cfg.ParentType == "ssh" { s.log.Printf("warn: socks udp not suppored for ssh") - } else { - s.udpSC = utils.NewServerChannelHost(*s.cfg.UDPLocal, s.log) - e := s.udpSC.ListenUDP(s.udpCallback) - if e != nil { - err = fmt.Errorf("init udp service fail, ERR: %s", e) - return - } - s.log.Printf("udp socks proxy on %s", s.udpSC.UDPListener.LocalAddr()) } return } @@ -203,11 +191,13 @@ func (s *Socks) StopService() { if e != nil { s.log.Printf("stop socks service crashed,%s", e) } else { - s.log.Printf("service socks stoped") + s.log.Printf("service socks stopped") } }() s.isStop = true - s.checker.Stop() + if *s.cfg.Parent != "" { + s.checker.Stop() + } if s.sshClient != nil { s.sshClient.Close() } @@ -220,6 +210,9 @@ func (s *Socks) StopService() { for _, c := range s.userConns.Items() { (*c.(*net.Conn)).Close() } + for _, c := range s.udpRelatedPacketConns.Items() { + (*c.(*net.UDPConn)).Close() + } } func (s *Socks) Start(args interface{}, log *logger.Logger) (err error) { s.log = log @@ -234,9 +227,6 @@ func (s *Socks) Start(args interface{}, log *logger.Logger) (err error) { if *s.cfg.Parent != "" { s.log.Printf("use %s parent %s", *s.cfg.ParentType, *s.cfg.Parent) } - if *s.cfg.UDPParent != "" { - s.log.Printf("use socks udp parent %s", *s.cfg.UDPParent) - } sc := utils.NewServerChannelHost(*s.cfg.Local, s.log) if *s.cfg.LocalType == "tcp" { err = sc.ListenTCP(s.socksConnCallback) @@ -255,165 +245,7 @@ func (s *Socks) Start(args interface{}, log *logger.Logger) (err error) { func (s *Socks) Clean() { s.StopService() } -func (s *Socks) UDPKey() []byte { - return s.cfg.KeyBytes[:32] -} -func (s *Socks) udpCallback(b []byte, localAddr, srcAddr *net.UDPAddr) { - rawB := b - var err error - if *s.cfg.LocalType == "tls" { - //decode b - rawB, err = goaes.Decrypt(s.UDPKey(), b) - if err != nil { - s.log.Printf("decrypt udp packet fail from %s", srcAddr.String()) - return - } - } - p, err := socks.ParseUDPPacket(rawB) - s.log.Printf("udp revecived:%v", len(p.Data())) - if err != nil { - s.log.Printf("parse udp packet fail, ERR:%s", err) - return - } - //防止死循环 - if s.IsDeadLoop((*localAddr).String(), p.Host()) { - s.log.Printf("dead loop detected , %s", p.Host()) - return - } - //s.log.Printf("##########udp to -> %s:%s###########", p.Host(), p.Port()) - if *s.cfg.Parent != "" { - //有上级代理,转发给上级 - if *s.cfg.ParentType == "tls" { - //encode b - rawB, err = goaes.Encrypt(s.UDPKey(), rawB) - if err != nil { - s.log.Printf("encrypt udp data fail to %s", *s.cfg.Parent) - return - } - } - parent := *s.cfg.UDPParent - if parent == "" { - parent = *s.cfg.Parent - } - dstAddr, err := net.ResolveUDPAddr("udp", s.Resolve(parent)) - if err != nil { - s.log.Printf("can't resolve address: %s", err) - return - } - clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} - conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) - if err != nil { - s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) - return - } - conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*5))) - _, err = conn.Write(rawB) - conn.SetDeadline(time.Time{}) - s.log.Printf("udp request:%v", len(rawB)) - if err != nil { - s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) - conn.Close() - return - } - //s.log.Printf("send udp packet to %s success", dstAddr.String()) - buf := make([]byte, 10*1024) - conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - length, _, err := conn.ReadFromUDP(buf) - conn.SetDeadline(time.Time{}) - if err != nil { - s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) - conn.Close() - return - } - respBody := buf[0:length] - s.log.Printf("udp response:%v", len(respBody)) - //s.log.Printf("revecived udp packet from %s", dstAddr.String()) - if *s.cfg.ParentType == "tls" { - //decode b - respBody, err = goaes.Decrypt(s.UDPKey(), respBody) - if err != nil { - s.log.Printf("encrypt udp data fail to %s", *s.cfg.Parent) - conn.Close() - return - } - } - if *s.cfg.LocalType == "tls" { - d, err := goaes.Encrypt(s.UDPKey(), respBody) - if err != nil { - s.log.Printf("encrypt udp data fail from %s", dstAddr.String()) - conn.Close() - return - } - s.udpSC.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - s.udpSC.UDPListener.WriteToUDP(d, srcAddr) - s.udpSC.UDPListener.SetDeadline(time.Time{}) - s.log.Printf("udp reply:%v", len(d)) - d = nil - } else { - s.udpSC.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - s.udpSC.UDPListener.WriteToUDP(respBody, srcAddr) - s.udpSC.UDPListener.SetDeadline(time.Time{}) - s.log.Printf("udp reply:%v", len(respBody)) - } - - } else { - //本地代理 - dstAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(s.Resolve(p.Host()), p.Port())) - if err != nil { - s.log.Printf("can't resolve address: %s", err) - return - } - clientSrcAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} - conn, err := net.DialUDP("udp", clientSrcAddr, dstAddr) - if err != nil { - s.log.Printf("connect to udp %s fail,ERR:%s", dstAddr.String(), err) - return - } - conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout*3))) - _, err = conn.Write(p.Data()) - conn.SetDeadline(time.Time{}) - s.log.Printf("udp send:%v", len(p.Data())) - if err != nil { - s.log.Printf("send udp packet to %s fail,ERR:%s", dstAddr.String(), err) - conn.Close() - return - } - //s.log.Printf("send udp packet to %s success", dstAddr.String()) - buf := make([]byte, 10*1024) - conn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - length, _, err := conn.ReadFromUDP(buf) - conn.SetDeadline(time.Time{}) - - if err != nil { - s.log.Printf("read udp response from %s fail ,ERR:%s", dstAddr.String(), err) - conn.Close() - return - } - respBody := buf[0:length] - //封装来自真实服务器的数据,返回给访问者 - respPacket := p.NewReply(respBody) - //s.log.Printf("revecived udp packet from %s", dstAddr.String()) - if *s.cfg.LocalType == "tls" { - d, err := goaes.Encrypt(s.UDPKey(), respPacket) - if err != nil { - s.log.Printf("encrypt udp data fail from %s", dstAddr.String()) - conn.Close() - return - } - s.udpSC.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - s.udpSC.UDPListener.WriteToUDP(d, srcAddr) - s.udpSC.UDPListener.SetDeadline(time.Time{}) - d = nil - } else { - s.udpSC.UDPListener.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) - s.udpSC.UDPListener.WriteToUDP(respPacket, srcAddr) - s.udpSC.UDPListener.SetDeadline(time.Time{}) - } - s.log.Printf("udp reply:%v", len(respPacket)) - } - -} func (s *Socks) socksConnCallback(inConn net.Conn) { defer func() { if err := recover(); err != nil { @@ -438,7 +270,9 @@ func (s *Socks) socksConnCallback(inConn net.Conn) { if err != nil { methodReq.Reply(socks.Method_NONE_ACCEPTABLE) utils.CloseConn(&inConn) - s.log.Printf("new methods request fail,ERR: %s", err) + if err != io.EOF { + s.log.Printf("new methods request fail,ERR: %s", err) + } return } @@ -526,16 +360,7 @@ func (s *Socks) socksConnCallback(inConn net.Conn) { } } -func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { - if *s.cfg.ParentType == "ssh" { - utils.CloseConn(inConn) - return - } - host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) - _, port, _ := net.SplitHostPort(s.udpSC.UDPListener.LocalAddr().String()) - s.log.Printf("proxy udp on %s", net.JoinHostPort(host, port)) - request.UDPReply(socks.REP_SUCCESS, net.JoinHostPort(host, port)) -} + func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { var outConn net.Conn var err interface{} @@ -554,7 +379,7 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque return } if *s.cfg.Always { - outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr()) + outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr(), true) } else { if *s.cfg.Parent != "" { host, _, _ := net.SplitHostPort(request.Addr()) @@ -562,12 +387,14 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque if utils.IsIternalIP(host, *s.cfg.Always) { useProxy = false } else { - k := s.Resolve(request.Addr()) - s.checker.Add(request.Addr(), k) - useProxy, _, _ = s.checker.IsBlocked(k) + var isInMap bool + useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr()) + if !isInMap { + s.checker.Add(request.Addr(), s.Resolve(request.Addr())) + } } if useProxy { - outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr()) + outConn, err = s.getOutConn(methodReq.Bytes(), request.Bytes(), request.Addr(), true) } else { outConn, err = utils.ConnectHost(s.Resolve(request.Addr()), *s.cfg.Timeout) } @@ -607,7 +434,7 @@ func (s *Socks) proxyTCP(inConn *net.Conn, methodReq socks.MethodsRequest, reque } s.userConns.Set(inAddr, inConn) } -func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn net.Conn, err interface{}) { +func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string, handshake bool) (outConn net.Conn, err interface{}) { switch *s.cfg.ParentType { case "kcp": fallthrough @@ -635,6 +462,9 @@ func (s *Socks) getOutConn(methodBytes, reqBytes []byte, host string) (outConn n Password: *s.cfg.ParentKey, }) } + if !handshake { + return + } var buf = make([]byte, 1024) //var n int outConn.SetDeadline(time.Now().Add(time.Millisecond * time.Duration(*s.cfg.Timeout))) @@ -772,7 +602,7 @@ func (s *Socks) IsDeadLoop(inLocalAddr string, host string) bool { if *s.cfg.DNSAddress != "" { outIPs = []net.IP{net.ParseIP(s.Resolve(outDomain))} } else { - outIPs, err = net.LookupIP(outDomain) + outIPs, err = utils.MyLookupIP(outDomain) } if err == nil { for _, ip := range outIPs { @@ -804,6 +634,7 @@ func (s *Socks) Resolve(address string) string { ip, err := s.domainResolver.Resolve(address) if err != nil { s.log.Printf("dns error %s , ERR:%s", address, err) + return address } return ip } diff --git a/services/socks/udp.go b/services/socks/udp.go new file mode 100644 index 0000000..ad71a01 --- /dev/null +++ b/services/socks/udp.go @@ -0,0 +1,321 @@ +package socks + +import ( + "crypto/md5" + "fmt" + "net" + "runtime/debug" + "strconv" + "strings" + "time" + + "github.com/snail007/goproxy/utils" + goaes "github.com/snail007/goproxy/utils/aes" + "github.com/snail007/goproxy/utils/socks" +) + +func (s *Socks) ParentUDPKey() (key []byte) { + switch *s.cfg.ParentType { + case "tcp": + if *s.cfg.ParentKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *Socks) LocalUDPKey() (key []byte) { + switch *s.cfg.LocalType { + case "tcp": + if *s.cfg.LocalKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *Socks) proxyUDP(inConn *net.Conn, methodReq socks.MethodsRequest, request socks.Request) { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + if *s.cfg.ParentType == "ssh" { + utils.CloseConn(inConn) + return + } + srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String()) + inconnRemoteAddr := (*inConn).RemoteAddr().String() + localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} + udpListener, err := net.ListenUDP("udp", localAddr) + if err != nil { + (*inConn).Close() + udpListener.Close() + s.log.Printf("udp bind fail , %s", err) + return + } + host, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) + _, port, _ := net.SplitHostPort(udpListener.LocalAddr().String()) + if len(*s.cfg.LocalIPS) > 0 { + host = (*s.cfg.LocalIPS)[0] + } + s.log.Printf("proxy udp on %s , for %s", net.JoinHostPort(host, port), inconnRemoteAddr) + request.UDPReply(socks.REP_SUCCESS, net.JoinHostPort(host, port)) + s.userConns.Set(inconnRemoteAddr, inConn) + var ( + outUDPConn *net.UDPConn + outconn net.Conn + outconnLocalAddr string + isClosedErr = func(err error) bool { + return err != nil && strings.Contains(err.Error(), "use of closed network connection") + } + destAddr *net.UDPAddr + ) + var clean = func(msg, err string) { + raddr := "" + if outUDPConn != nil { + raddr = outUDPConn.RemoteAddr().String() + outUDPConn.Close() + } + if msg != "" { + if raddr != "" { + s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr) + } else { + s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr) + } + } + (*inConn).Close() + udpListener.Close() + s.userConns.Remove(inconnRemoteAddr) + if outconn != nil { + outconn.Close() + } + if outconnLocalAddr != "" { + s.userConns.Remove(outconnLocalAddr) + } + } + defer clean("", "") + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + (*inConn).SetReadDeadline(time.Time{}) + if _, err := (*inConn).Read(buf); err != nil { + clean("udp related tcp conn disconnected with read", err.Error()) + } + }() + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + for { + (*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5)) + if _, err := (*inConn).Write([]byte{0x00}); err != nil { + clean("udp related tcp conn disconnected with write", err.Error()) + return + } + (*inConn).SetWriteDeadline(time.Time{}) + time.Sleep(time.Second * 5) + } + }() + useProxy := true + if *s.cfg.Parent != "" { + dstHost, _, _ := net.SplitHostPort(request.Addr()) + if utils.IsIternalIP(dstHost, *s.cfg.Always) { + useProxy = false + } else { + var isInMap bool + useProxy, isInMap, _, _ = s.checker.IsBlocked(request.Addr()) + if !isInMap { + s.checker.Add(request.Addr(), s.Resolve(request.Addr())) + } + } + } else { + useProxy = false + } + if useProxy { + //parent proxy + outconn, err := s.getOutConn(nil, nil, "", false) + if err != nil { + clean("connnect fail", fmt.Sprintf("%s", err)) + return + } + client := socks.NewClientConn(&outconn, "udp", request.Addr(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) + if err = client.Handshake(); err != nil { + clean("handshake fail", fmt.Sprintf("%s", err)) + return + } + //outconnRemoteAddr := outconn.RemoteAddr().String() + outconnLocalAddr = outconn.LocalAddr().String() + s.userConns.Set(outconnLocalAddr, &outconn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + outconn.SetReadDeadline(time.Time{}) + if _, err := outconn.Read(buf); err != nil { + clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err)) + } + }() + //forward to parent udp + //s.log.Printf("parent udp address %s", client.UDPAddr) + destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) + } + s.log.Printf("use proxy %v : udp %s", useProxy, request.Addr()) + //relay + for { + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + n, srcAddr, err := udpListener.ReadFromUDP(buf) + if err != nil { + s.log.Printf("udp listener read fail, %s", err.Error()) + if isClosedErr(err) { + return + } + continue + } + srcIP0, _, _ := net.SplitHostPort(srcAddr.String()) + //IP not match drop it + if srcIP != srcIP0 { + continue + } + p := socks.NewPacketUDP() + //convert data to raw + if len(s.udpLocalKey) > 0 { + var v []byte + v, err = goaes.Decrypt(s.udpLocalKey, buf[:n]) + if err == nil { + err = p.Parse(v) + } + } else { + err = p.Parse(buf[:n]) + } + //err = p.Parse(buf[:n]) + if err != nil { + s.log.Printf("udp listener parse packet fail, %s", err.Error()) + continue + } + + port, _ := strconv.Atoi(p.Port()) + + if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { + if destAddr == nil { + destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port} + } + outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) + if err != nil { + s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) + continue + } + s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + defer s.udpRelatedPacketConns.Remove(srcAddr.String()) + //out->local io copy + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + for { + n, err := outUDPConn.Read(buf) + if err != nil { + s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } + //var dlen = n + if useProxy { + //forward to local + var v []byte + //convert parent data to raw + if len(s.udpParentKey) > 0 { + v, err = goaes.Decrypt(s.udpParentKey, buf[:n]) + if err != nil { + s.log.Printf("udp outconn parse packet fail, %s", err.Error()) + continue + } + } else { + v = buf[:n] + } + //now v is raw, try convert v to local + if len(s.udpLocalKey) > 0 { + v, _ = goaes.Encrypt(s.udpLocalKey, v) + } + _, err = udpListener.WriteTo(v, srcAddr) + // _, err = udpListener.WriteTo(buf[:n], srcAddr) + } else { + rp := socks.NewPacketUDP() + rp.Build(destAddr.String(), buf[:n]) + v := rp.Bytes() + //dlen = len(v) + //rp.Bytes() v is raw, try convert to local + if len(s.udpLocalKey) > 0 { + v, _ = goaes.Encrypt(s.udpLocalKey, v) + } + _, err = udpListener.WriteTo(v, srcAddr) + } + + if err != nil { + s.udpRelatedPacketConns.Remove(srcAddr.String()) + s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } else { + //s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr) + } + } + }() + } else { + outUDPConn = v.(*net.UDPConn) + } + //local->out io copy + if useProxy { + //forward to parent + //p is raw, now convert it to parent + var v []byte + if len(s.udpParentKey) > 0 { + v, _ = goaes.Encrypt(s.udpParentKey, p.Bytes()) + } else { + v = p.Bytes() + } + _, err = outUDPConn.Write(v) + // _, err = outUDPConn.Write(p.Bytes()) + } else { + _, err = outUDPConn.Write(p.Data()) + } + if err != nil { + if isClosedErr(err) { + return + } + s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr) + continue + } else { + //s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr) + } + } + +} diff --git a/services/sps/socksudp.go b/services/sps/socksudp.go new file mode 100644 index 0000000..e59c7d9 --- /dev/null +++ b/services/sps/socksudp.go @@ -0,0 +1,306 @@ +package sps + +import ( + "crypto/md5" + "fmt" + "net" + "runtime/debug" + "strconv" + "strings" + "time" + + "github.com/snail007/goproxy/utils" + goaes "github.com/snail007/goproxy/utils/aes" + "github.com/snail007/goproxy/utils/conncrypt" + "github.com/snail007/goproxy/utils/socks" +) + +func (s *SPS) ParentUDPKey() (key []byte) { + switch *s.cfg.ParentType { + case "tcp": + if *s.cfg.ParentKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.ParentKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *SPS) LocalUDPKey() (key []byte) { + switch *s.cfg.LocalType { + case "tcp": + if *s.cfg.LocalKey != "" { + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.LocalKey))) + return []byte(v)[:24] + } + case "tls": + return s.cfg.KeyBytes[:24] + case "kcp": + v := fmt.Sprintf("%x", md5.Sum([]byte(*s.cfg.KCP.Key))) + return []byte(v)[:24] + } + return +} +func (s *SPS) proxyUDP(inConn *net.Conn, serverConn *socks.ServerConn) { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp local->out io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + if *s.cfg.ParentType == "ssh" { + utils.CloseConn(inConn) + return + } + srcIP, _, _ := net.SplitHostPort((*inConn).RemoteAddr().String()) + inconnRemoteAddr := (*inConn).RemoteAddr().String() + localAddr := &net.UDPAddr{IP: net.IPv4zero, Port: 0} + udpListener := serverConn.UDPConnListener + s.log.Printf("proxy udp on %s , for %s", udpListener.LocalAddr(), inconnRemoteAddr) + s.userConns.Set(inconnRemoteAddr, inConn) + var ( + outUDPConn *net.UDPConn + outconn net.Conn + outconnLocalAddr string + isClosedErr = func(err error) bool { + return err != nil && strings.Contains(err.Error(), "use of closed network connection") + } + destAddr *net.UDPAddr + ) + var clean = func(msg, err string) { + raddr := "" + if outUDPConn != nil { + raddr = outUDPConn.RemoteAddr().String() + outUDPConn.Close() + } + if msg != "" { + if raddr != "" { + s.log.Printf("%s , %s , %s -> %s", msg, err, inconnRemoteAddr, raddr) + } else { + s.log.Printf("%s , %s , from : %s", msg, err, inconnRemoteAddr) + } + } + (*inConn).Close() + udpListener.Close() + s.userConns.Remove(inconnRemoteAddr) + if outconn != nil { + outconn.Close() + } + if outconnLocalAddr != "" { + s.userConns.Remove(outconnLocalAddr) + } + } + defer clean("", "") + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + (*inConn).SetReadDeadline(time.Time{}) + if _, err := (*inConn).Read(buf); err != nil { + clean("udp related tcp conn disconnected with read", err.Error()) + } + }() + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related client tcp conn write crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + for { + (*inConn).SetWriteDeadline(time.Now().Add(time.Second * 5)) + if _, err := (*inConn).Write([]byte{0x00}); err != nil { + clean("udp related tcp conn disconnected with write", err.Error()) + return + } + (*inConn).SetWriteDeadline(time.Time{}) + time.Sleep(time.Second * 5) + } + }() + //parent proxy + outconn, err := s.outPool.Get() + //outconn, err := s.GetParentConn(nil, nil, "", false) + if err != nil { + clean("connnect fail", fmt.Sprintf("%s", err)) + return + } + if *s.cfg.ParentCompress { + outconn = utils.NewCompConn(outconn) + } + if *s.cfg.ParentKey != "" { + outconn = conncrypt.New(outconn, &conncrypt.Config{ + Password: *s.cfg.ParentKey, + }) + } + + s.log.Printf("connect %s for udp", serverConn.Target()) + //socks client + var client *socks.ClientConn + auth := serverConn.AuthData() + if *s.cfg.ParentAuth != "" { + a := strings.Split(*s.cfg.ParentAuth, ":") + if len(a) != 2 { + err = fmt.Errorf("parent auth data format error") + return + } + client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &socks.Auth{User: a[0], Password: a[1]}, nil) + } else { + if !s.IsBasicAuth() && auth.Password != "" && auth.User != "" { + client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), &auth, nil) + } else { + client = socks.NewClientConn(&outconn, "udp", serverConn.Target(), time.Millisecond*time.Duration(*s.cfg.Timeout), nil, nil) + } + } + + if err = client.Handshake(); err != nil { + clean("handshake fail", fmt.Sprintf("%s", err)) + return + } + + //outconnRemoteAddr := outconn.RemoteAddr().String() + outconnLocalAddr = outconn.LocalAddr().String() + s.userConns.Set(outconnLocalAddr, &outconn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp related parent tcp conn read crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + buf := make([]byte, 1) + outconn.SetReadDeadline(time.Time{}) + if _, err := outconn.Read(buf); err != nil { + clean("udp parent tcp conn disconnected", fmt.Sprintf("%s", err)) + } + }() + //forward to parent udp + //s.log.Printf("parent udp address %s", client.UDPAddr) + destAddr, _ = net.ResolveUDPAddr("udp", client.UDPAddr) + //relay + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + for { + n, srcAddr, err := udpListener.ReadFromUDP(buf) + if err != nil { + s.log.Printf("udp listener read fail, %s", err.Error()) + if isClosedErr(err) { + return + } + continue + } + srcIP0, _, _ := net.SplitHostPort(srcAddr.String()) + //IP not match drop it + if srcIP != srcIP0 { + continue + } + p := socks.NewPacketUDP() + //convert data to raw + if len(s.udpLocalKey) > 0 { + var v []byte + v, err = goaes.Decrypt(s.udpLocalKey, buf[:n]) + if err == nil { + err = p.Parse(v) + } + } else { + err = p.Parse(buf[:n]) + } + if err != nil { + s.log.Printf("udp listener parse packet fail, %s", err.Error()) + continue + } + + port, _ := strconv.Atoi(p.Port()) + + if v, ok := s.udpRelatedPacketConns.Get(srcAddr.String()); !ok { + if destAddr == nil { + destAddr = &net.UDPAddr{IP: net.ParseIP(p.Host()), Port: port} + } + outUDPConn, err = net.DialUDP("udp", localAddr, destAddr) + if err != nil { + s.log.Printf("create out udp conn fail , %s , from : %s", err, srcAddr) + continue + } + s.udpRelatedPacketConns.Set(srcAddr.String(), outUDPConn) + go func() { + defer func() { + if e := recover(); e != nil { + s.log.Printf("udp out->local io copy crashed:\n%s\n%s", e, string(debug.Stack())) + } + }() + defer s.udpRelatedPacketConns.Remove(srcAddr.String()) + //out->local io copy + buf := utils.LeakyBuffer.Get() + defer utils.LeakyBuffer.Put(buf) + for { + outUDPConn.SetReadDeadline(time.Now().Add(time.Second * 5)) + n, err := outUDPConn.Read(buf) + outUDPConn.SetReadDeadline(time.Time{}) + if err != nil { + s.log.Printf("read out udp data fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } + //var dlen = n + //forward to local + var v []byte + //convert parent data to raw + if len(s.udpParentKey) > 0 { + v, err = goaes.Decrypt(s.udpParentKey, buf[:n]) + if err != nil { + s.log.Printf("udp outconn parse packet fail, %s", err.Error()) + continue + } + } else { + v = buf[:n] + } + //now v is raw, try convert v to local + if len(s.udpLocalKey) > 0 { + v, _ = goaes.Encrypt(s.udpLocalKey, v) + } + _, err = udpListener.WriteTo(v, srcAddr) + // _, err = udpListener.WriteTo(buf[:n], srcAddr) + + if err != nil { + s.udpRelatedPacketConns.Remove(srcAddr.String()) + s.log.Printf("write out data to local fail , %s , from : %s", err, srcAddr) + if isClosedErr(err) { + return + } + continue + } else { + //s.log.Printf("send udp data to local success , len %d, for : %s", dlen, srcAddr) + } + } + }() + } else { + outUDPConn = v.(*net.UDPConn) + } + //local->out io copy + //forward to parent + //p is raw, now convert it to parent + var v []byte + if len(s.udpParentKey) > 0 { + v, _ = goaes.Encrypt(s.udpParentKey, p.Bytes()) + } else { + v = p.Bytes() + } + _, err = outUDPConn.Write(v) + // _, err = outUDPConn.Write(p.Bytes()) + if err != nil { + if isClosedErr(err) { + return + } + s.log.Printf("send out udp data fail , %s , from : %s", err, srcAddr) + continue + } else { + //s.log.Printf("send udp data to remote success , len %d, for : %s", len(p.Data()), srcAddr) + } + } + +} diff --git a/services/sps/sps.go b/services/sps/sps.go index 68591c7..343f69b 100644 --- a/services/sps/sps.go +++ b/services/sps/sps.go @@ -17,6 +17,7 @@ import ( "github.com/snail007/goproxy/services/kcpcfg" "github.com/snail007/goproxy/utils" "github.com/snail007/goproxy/utils/conncrypt" + "github.com/snail007/goproxy/utils/sni" "github.com/snail007/goproxy/utils/socks" ) @@ -52,22 +53,26 @@ type SPSArgs struct { DisableSocks5 *bool } type SPS struct { - outPool utils.OutConn - cfg SPSArgs - domainResolver utils.DomainResolver - basicAuth utils.BasicAuth - serverChannels []*utils.ServerChannel - userConns utils.ConcurrentMap - log *logger.Logger + outPool utils.OutConn + cfg SPSArgs + domainResolver utils.DomainResolver + basicAuth utils.BasicAuth + serverChannels []*utils.ServerChannel + userConns utils.ConcurrentMap + log *logger.Logger + udpRelatedPacketConns utils.ConcurrentMap + udpLocalKey []byte + udpParentKey []byte } func NewSPS() services.Service { return &SPS{ - outPool: utils.OutConn{}, - cfg: SPSArgs{}, - basicAuth: utils.BasicAuth{}, - serverChannels: []*utils.ServerChannel{}, - userConns: utils.NewConcurrentMap(), + outPool: utils.OutConn{}, + cfg: SPSArgs{}, + basicAuth: utils.BasicAuth{}, + serverChannels: []*utils.ServerChannel{}, + userConns: utils.NewConcurrentMap(), + udpRelatedPacketConns: utils.NewConcurrentMap(), } } func (s *SPS) CheckArgs() (err error) { @@ -92,6 +97,8 @@ func (s *SPS) CheckArgs() (err error) { } } } + s.udpLocalKey = s.LocalUDPKey() + s.udpParentKey = s.ParentUDPKey() return } func (s *SPS) InitService() (err error) { @@ -123,7 +130,7 @@ func (s *SPS) StopService() { if e != nil { s.log.Printf("stop sps service crashed,%s", e) } else { - s.log.Printf("service sps stoped") + s.log.Printf("service sps stopped") } }() for _, sc := range s.serverChannels { @@ -209,6 +216,11 @@ func (s *SPS) callback(inConn net.Conn) { } } func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { + enableUDP := *s.cfg.ParentServiceType == "socks" + udpIP, _, _ := net.SplitHostPort((*inConn).LocalAddr().String()) + if len(*s.cfg.LocalIPS) > 0 { + udpIP = (*s.cfg.LocalIPS)[0] + } bInConn := utils.NewBufferedConn(*inConn) //important //action read will regist read event to system, @@ -218,7 +230,7 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { bInConn.ReadByte() bInConn.UnreadByte() - n := 8 + n := 2048 if n > bInConn.Buffered() { n = bInConn.Buffered() } @@ -228,12 +240,12 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { (*inConn).Close() return } - + isSNI, _ := sni.ServerNameFromBytes(h) *inConn = bInConn address := "" var auth socks.Auth var forwardBytes []byte - //fmt.Printf("%v", header) + //fmt.Printf("%v", h) if utils.IsSocks5(h) { if *s.cfg.DisableSocks5 { (*inConn).Close() @@ -242,16 +254,20 @@ func (s *SPS) OutToTCP(inConn *net.Conn) (err error) { //socks5 server var serverConn *socks.ServerConn if s.IsBasicAuth() { - serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, "", nil) + serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), &s.basicAuth, enableUDP, udpIP, nil) } else { - serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, "", nil) + serverConn = socks.NewServerConn(inConn, time.Millisecond*time.Duration(*s.cfg.Timeout), nil, enableUDP, udpIP, nil) } if err = serverConn.Handshake(); err != nil { return } address = serverConn.Target() auth = serverConn.AuthData() - } else if utils.IsHTTP(h) { + if serverConn.IsUDP() { + s.proxyUDP(inConn, serverConn) + return + } + } else if utils.IsHTTP(h) || isSNI != "" { if *s.cfg.DisableHTTP { (*inConn).Close() return @@ -495,6 +511,7 @@ func (s *SPS) Resolve(address string) string { ip, err := s.domainResolver.Resolve(address) if err != nil { s.log.Printf("dns error %s , ERR:%s", address, err) + return address } return ip } diff --git a/services/tcp/tcp.go b/services/tcp/tcp.go index 652b539..8f7dd75 100644 --- a/services/tcp/tcp.go +++ b/services/tcp/tcp.go @@ -74,7 +74,7 @@ func (s *TCP) StopService() { if e != nil { s.log.Printf("stop tcp service crashed,%s", e) } else { - s.log.Printf("service tcp stoped") + s.log.Printf("service tcp stopped") } }() s.isStop = true diff --git a/services/tunnel/tunnel_bridge.go b/services/tunnel/tunnel_bridge.go index 8f27284..d409a61 100644 --- a/services/tunnel/tunnel_bridge.go +++ b/services/tunnel/tunnel_bridge.go @@ -70,7 +70,7 @@ func (s *TunnelBridge) StopService() { if e != nil { s.log.Printf("stop tbridge service crashed,%s", e) } else { - s.log.Printf("service tbridge stoped") + s.log.Printf("service tbridge stopped") } }() s.isStop = true diff --git a/services/tunnel/tunnel_client.go b/services/tunnel/tunnel_client.go index 6e80dfd..d759e2e 100644 --- a/services/tunnel/tunnel_client.go +++ b/services/tunnel/tunnel_client.go @@ -70,7 +70,7 @@ func (s *TunnelClient) StopService() { if e != nil { s.log.Printf("stop tclient service crashed,%s", e) } else { - s.log.Printf("service tclient stoped") + s.log.Printf("service tclient stopped") } }() s.isStop = true diff --git a/services/tunnel/tunnel_server.go b/services/tunnel/tunnel_server.go index 594033e..858f168 100644 --- a/services/tunnel/tunnel_server.go +++ b/services/tunnel/tunnel_server.go @@ -183,7 +183,7 @@ func (s *TunnelServer) StopService() { if e != nil { s.log.Printf("stop server service crashed,%s", e) } else { - s.log.Printf("service server stoped") + s.log.Printf("service server stopped") } }() s.isStop = true diff --git a/services/udp/udp.go b/services/udp/udp.go index a9388e2..cf2d46f 100644 --- a/services/udp/udp.go +++ b/services/udp/udp.go @@ -73,7 +73,7 @@ func (s *UDP) StopService() { if e != nil { s.log.Printf("stop udp service crashed,%s", e) } else { - s.log.Printf("service udp stoped") + s.log.Printf("service udp stopped") } }() s.isStop = true diff --git a/utils/cert/cert.go b/utils/cert/cert.go index 58b2356..ac25ba5 100644 --- a/utils/cert/cert.go +++ b/utils/cert/cert.go @@ -73,7 +73,7 @@ func CreateSignCert(rootCa *x509.Certificate, rootKey *rsa.PrivateKey, domainOrI buf := x509.MarshalPKCS1PrivateKey(priKey) keyPem := &pem.Block{ - Type: "PRIVATE KEY", + Type: "RSA PRIVATE KEY", Bytes: buf, } keyBytes = pem.EncodeToMemory(keyPem) @@ -109,7 +109,7 @@ func CreateCa(organization string, expireDays int) (certBytes []byte, keyBytes [ NotAfter: time.Now().Add(time.Hour * 24 * time.Duration(expireDays)), KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth}, BasicConstraintsValid: true, IsCA: true, } diff --git a/utils/conncrypt/conncrypt.go b/utils/conncrypt/conncrypt.go index f3115e7..82641e8 100644 --- a/utils/conncrypt/conncrypt.go +++ b/utils/conncrypt/conncrypt.go @@ -12,8 +12,8 @@ import ( ) //Confg defaults -const DefaultIterations = 2048 -const DefaultKeySize = 32 //256bits +const DefaultIterations = 1024 +const DefaultKeySize = 24 //256bits var DefaultHashFunc = sha256.New var DefaultSalt = []byte(` (;QUHj.BQ?RXzYSO]ifkXp/G!kFmWyXyEV6Nt!d|@bo+N$L9+ 0xffff { + if s.network == "tcp" && (port < 1 || port > 0xffff) { return errors.New("proxy: port number out of range: " + portStr) } @@ -168,14 +168,14 @@ func (s *ClientConn) Handshake() error { } p := binary.BigEndian.Uint16([]byte{buf[0], buf[1]}) //log.Printf("%v", p) - s.udpAddr = net.JoinHostPort(ipStr, fmt.Sprintf("%d", p)) + s.UDPAddr = net.JoinHostPort(ipStr, fmt.Sprintf("%d", p)) //log.Printf("%v", s.udpAddr) (*s.conn).SetDeadline(time.Time{}) return nil } func (s *ClientConn) SendUDP(data []byte, addr string) (respData []byte, err error) { - c, err := net.DialTimeout("udp", s.udpAddr, s.timeout) + c, err := net.DialTimeout("udp", s.UDPAddr, s.timeout) if err != nil { return } diff --git a/utils/socks/server.go b/utils/socks/server.go index 67f26bf..8808562 100644 --- a/utils/socks/server.go +++ b/utils/socks/server.go @@ -2,10 +2,11 @@ package socks import ( "fmt" - "github.com/snail007/goproxy/utils" "net" "strings" "time" + + "github.com/snail007/goproxy/utils" ) const ( @@ -54,26 +55,27 @@ type ServerConn struct { methods []uint8 method uint8 //request - cmd uint8 - reserve uint8 - addressType uint8 - dstAddr string - dstPort string - dstHost string - udpAddress string + cmd uint8 + reserve uint8 + addressType uint8 + dstAddr string + dstPort string + dstHost string + UDPConnListener *net.UDPConn + enableUDP bool + udpIP string } -func NewServerConn(conn *net.Conn, timeout time.Duration, auth *utils.BasicAuth, udpAddress string, header []byte) *ServerConn { - if udpAddress == "" { - udpAddress = "0.0.0.0:16666" - } +func NewServerConn(conn *net.Conn, timeout time.Duration, auth *utils.BasicAuth, enableUDP bool, udpHost string, header []byte) *ServerConn { + s := &ServerConn{ - conn: conn, - timeout: timeout, - auth: auth, - header: header, - ver: VERSION_V5, - udpAddress: udpAddress, + conn: conn, + timeout: timeout, + auth: auth, + header: header, + ver: VERSION_V5, + enableUDP: enableUDP, + udpIP: udpHost, } return s @@ -84,6 +86,12 @@ func (s *ServerConn) Close() { func (s *ServerConn) AuthData() Auth { return Auth{s.user, s.password} } +func (s *ServerConn) IsUDP() bool { + return s.cmd == CMD_ASSOCIATE +} +func (s *ServerConn) IsTCP() bool { + return s.cmd == CMD_CONNECT +} func (s *ServerConn) Method() uint8 { return s.method } @@ -205,11 +213,29 @@ func (s *ServerConn) Handshake() (err error) { return } case CMD_ASSOCIATE: - err = request.UDPReply(REP_SUCCESS, s.udpAddress) + if !s.enableUDP { + request.UDPReply(REP_UNKNOWN, "0.0.0.0:0") + if err != nil { + err = fmt.Errorf("UDPReply REP_UNKNOWN to %s fail,ERR: %s", remoteAddr, err) + return + } + err = fmt.Errorf("cmd associate not supported, form: %s", remoteAddr) + return + } + a, _ := net.ResolveUDPAddr("udp", ":0") + s.UDPConnListener, err = net.ListenUDP("udp", a) + if err != nil { + request.UDPReply(REP_UNKNOWN, "0.0.0.0:0") + err = fmt.Errorf("udp bind fail,ERR: %s , for %s", err, remoteAddr) + return + } + _, port, _ := net.SplitHostPort(s.UDPConnListener.LocalAddr().String()) + err = request.UDPReply(REP_SUCCESS, net.JoinHostPort(s.udpIP, port)) if err != nil { err = fmt.Errorf("UDPReply REP_SUCCESS to %s fail,ERR: %s", remoteAddr, err) return } + } //fill socks info diff --git a/utils/structs.go b/utils/structs.go index fe67135..06a1ead 100644 --- a/utils/structs.go +++ b/utils/structs.go @@ -33,15 +33,11 @@ type Checker struct { log *logger.Logger } type CheckerItem struct { - IsHTTPS bool - Method string - URL string Domain string - Host string - Data []byte + Address string SuccessCount uint FailCount uint - Key string + Lasttime int64 } //NewChecker args: @@ -101,17 +97,23 @@ func (c *Checker) start() { //log.Printf("check %s", item.Host) var conn net.Conn var err error - conn, err = ConnectHost(item.Host, c.timeout) + var now = time.Now().Unix() + conn, err = ConnectHost(item.Address, c.timeout) if err == nil { conn.SetDeadline(time.Now().Add(time.Millisecond)) conn.Close() } + if now-item.Lasttime > 1800 { + item.FailCount = 0 + item.SuccessCount = 0 + } if err != nil { item.FailCount = item.FailCount + 1 } else { item.SuccessCount = item.SuccessCount + 1 } - c.data.Set(item.Host, item) + item.Lasttime = now + c.data.Set(item.Domain, item) } }(v.(CheckerItem)) } @@ -124,32 +126,37 @@ func (c *Checker) start() { } func (c *Checker) isNeedCheck(item CheckerItem) bool { var minCount uint = 5 - if (item.SuccessCount >= minCount && item.SuccessCount > item.FailCount) || - (item.FailCount >= minCount && item.SuccessCount > item.FailCount) || - c.domainIsInMap(item.Host, false) || - c.domainIsInMap(item.Host, true) { + var now = time.Now().Unix() + if (item.SuccessCount >= minCount && item.SuccessCount > item.FailCount && now-item.Lasttime < 1800) || + (item.FailCount >= minCount && item.SuccessCount > item.FailCount && now-item.Lasttime < 1800) || + c.domainIsInMap(item.Domain, false) || + c.domainIsInMap(item.Domain, true) { return false } return true } -func (c *Checker) IsBlocked(address string) (blocked bool, failN, successN uint) { - if c.domainIsInMap(address, true) { - //log.Printf("%s in blocked ? true", address) - return true, 0, 0 +func (c *Checker) IsBlocked(domain string) (blocked, isInMap bool, failN, successN uint) { + h, _, _ := net.SplitHostPort(domain) + if h != "" { + domain = h } - if c.domainIsInMap(address, false) { + if c.domainIsInMap(domain, true) { + //log.Printf("%s in blocked ? true", address) + return true, true, 0, 0 + } + if c.domainIsInMap(domain, false) { //log.Printf("%s in direct ? true", address) - return false, 0, 0 + return false, true, 0, 0 } - _item, ok := c.data.Get(address) + _item, ok := c.data.Get(domain) if !ok { //log.Printf("%s not in map, blocked true", address) - return true, 0, 0 + return true, false, 0, 0 } item := _item.(CheckerItem) - return item.FailCount >= item.SuccessCount, item.FailCount, item.SuccessCount + return (item.FailCount >= item.SuccessCount) && (time.Now().Unix()-item.Lasttime < 1800), true, item.FailCount, item.SuccessCount } func (c *Checker) domainIsInMap(address string, blockedMap bool) bool { u, err := url.Parse("http://" + address) @@ -174,16 +181,20 @@ func (c *Checker) domainIsInMap(address string, blockedMap bool) bool { } return false } -func (c *Checker) Add(key, address string) { - if c.domainIsInMap(key, false) || c.domainIsInMap(key, true) { +func (c *Checker) Add(domain, address string) { + h, _, _ := net.SplitHostPort(domain) + if h != "" { + domain = h + } + if c.domainIsInMap(domain, false) || c.domainIsInMap(domain, true) { return } var item CheckerItem item = CheckerItem{ - Host: address, - Key: key, + Domain: domain, + Address: address, } - c.data.SetIfAbsent(item.Host, item) + c.data.SetIfAbsent(item.Domain, item) } type BasicAuth struct { @@ -329,6 +340,7 @@ type HTTPRequest struct { isBasicAuth bool basicAuth *BasicAuth log *logger.Logger + IsSNI bool } func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth *BasicAuth, log *logger.Logger, header ...[]byte) (req HTTPRequest, err error) { @@ -360,6 +372,7 @@ func NewHTTPRequest(inConn *net.Conn, bufSize int, isBasicAuth bool, basicAuth * //sni success req.Method = "SNI" req.hostOrURL = "https://" + serverName + ":443" + req.IsSNI = true } else { //sni fail , try http index := bytes.IndexByte(req.HeadBuf, '\n')