├── .github └── workflows │ └── go.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── go.mod ├── helper_linux.go ├── helper_windows.go ├── main.go └── pack.sh /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a golang project 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go 3 | 4 | name: Go 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Set up Go 20 | uses: actions/setup-go@v3 21 | with: 22 | go-version: 1.24 23 | 24 | - name: Build 25 | run: | 26 | go mod tidy 27 | go build -v ./... 28 | 29 | - name: Test 30 | run: go test -v ./... 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang AS build-env 2 | 3 | RUN GO111MODULE=off go get -u github.com/esrrhs/yellowsocks 4 | RUN GO111MODULE=off go get -u github.com/esrrhs/yellowsocks/... 5 | RUN GO111MODULE=off go install github.com/esrrhs/yellowsocks 6 | 7 | FROM debian 8 | COPY --from=build-env /go/bin/yellowsocks . 9 | WORKDIR ./ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 zhao xin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yellowsocks 2 | 3 | [](https://github.com/esrrhs/yellowsocks) 4 | [](https://github.com/esrrhs/yellowsocks) 5 | [![Go Report Card](https://goreportcard.com/badge/github.com/esrrhs/yellowsocks)](https://goreportcard.com/report/github.com/esrrhs/yellowsocks) 6 | [](https://github.com/esrrhs/yellowsocks/releases) 7 | [](https://github.com/esrrhs/yellowsocks/releases) 8 | [](https://hub.docker.com/repository/docker/esrrhs/yellowsocks) 9 | [](https://github.com/esrrhs/yellowsocks/actions) 10 | 11 | yellowsocks类似于[redsocks](https://github.com/darkk/redsocks),是依赖iptables把tcp转换为socks5的全局代理工具。 12 | 13 | # 配置代理 14 | 在linux或者路由器上启动,指定监听端口、socks5的地址端口。 15 | ``` 16 | ./yellowsocks -l :4455 -t 127.0.0.1:1080 17 | ``` 18 | # 配置iptables 19 | ``` 20 | 21 | *nat 22 | 23 | :PREROUTING ACCEPT [0:0] 24 | 25 | :INPUT ACCEPT [0:0] 26 | 27 | :OUTPUT ACCEPT [0:0] 28 | 29 | :POSTROUTING ACCEPT [0:0] 30 | 31 | :REDSOCKS - [0:0] 32 | 33 | # Redirect all output through redsocks 34 | 35 | -A OUTPUT -p tcp -j REDSOCKS 36 | 37 | # Whitelist LANs and some other reserved addresses. 38 | 39 | # https://en.wikipedia.org/wiki/Reserved_IP_addresses#Reserved_IPv4_addresses 40 | 41 | -A REDSOCKS -d 0.0.0.0/8 -j RETURN 42 | 43 | -A REDSOCKS -d 10.0.0.0/8 -j RETURN 44 | 45 | -A REDSOCKS -d 127.0.0.0/8 -j RETURN 46 | 47 | -A REDSOCKS -d 169.254.0.0/16 -j RETURN 48 | 49 | -A REDSOCKS -d 172.16.0.0/12 -j RETURN 50 | 51 | -A REDSOCKS -d 192.168.0.0/16 -j RETURN 52 | 53 | -A REDSOCKS -d 224.0.0.0/4 -j RETURN 54 | 55 | -A REDSOCKS -d 240.0.0.0/4 -j RETURN 56 | 57 | -A REDSOCKS -d {socks5的ip地址} -j RETURN 58 | 59 | -A REDSOCKS -p tcp --dport {socks5的端口} -j RETURN 60 | 61 | # Redirect everything else to redsocks port 62 | 63 | -A REDSOCKS -p tcp -j REDIRECT --to-ports 4455 64 | 65 | COMMIT 66 | 67 | ``` 68 | 69 | # openwrt配置 70 | * 编译yellowsocks成路由器对应版本,如GOOS=linux GOARCH=mipsle go build 71 | * 部分CPU需要在kernel里打开FPU,重新编译openwrt固件,编译方法参考官网 72 | * 路由器启动后,在/etc/firewall.user添加转发规则 73 | ``` 74 | iptables -t nat -N YELLOWSOCKS 75 | iptables -t nat -A PREROUTING -i br-lan -p tcp -j YELLOWSOCKS 76 | 77 | # Do not redirect traffic to the followign address ranges 78 | iptables -t nat -A YELLOWSOCKS -d 0.0.0.0/8 -j RETURN 79 | iptables -t nat -A YELLOWSOCKS -d 10.0.0.0/8 -j RETURN 80 | iptables -t nat -A YELLOWSOCKS -d 127.0.0.0/8 -j RETURN 81 | iptables -t nat -A YELLOWSOCKS -d 169.254.0.0/16 -j RETURN 82 | iptables -t nat -A YELLOWSOCKS -d 172.16.0.0/16 -j RETURN 83 | iptables -t nat -A YELLOWSOCKS -d 192.168.0.0/16 -j RETURN 84 | iptables -t nat -A YELLOWSOCKS -d 224.0.0.0/4 -j RETURN 85 | iptables -t nat -A YELLOWSOCKS -d 240.0.0.0/4 -j RETURN 86 | 87 | # Redirect all kinds of traffic 88 | iptables -t nat -A YELLOWSOCKS -p tcp -j REDIRECT --to-ports 4455 89 | ``` 90 | * 启动yellowsocks 91 | ``` 92 | ./yellowsocks -l :4455 -t sock5的ip:sock5的端口 -nolog 1 -noprint 1 93 | ``` 94 | * 如果上不去网,说明有dns污染,可以通过pingtunnel解决 95 | * 设置openwrt自带的dnsmasq转发 96 | ``` 97 | uci add_list dhcp.@dnsmasq[0].server="127.0.0.1#5353" 98 | uci set dhcp.@dnsmasq[0].noresolv="1" 99 | uci set dhcp.@dnsmasq[0].localuse="1" 100 | uci commit dhcp 101 | /etc/init.d/dnsmasq restart 102 | ``` 103 | * 启动pingtunnel,转发dns到远端 104 | ``` 105 | ./pingtunnel -type client -l :5353 -s yourserver -t 8.8.8.8:53 -key yourkey -nolog 1 -noprint 1 106 | ``` 107 | * 或者参考[yellowdns](https://github.com/esrrhs/yellowdns) 108 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/esrrhs/yellowsocks 2 | 3 | go 1.24.1 4 | 5 | require github.com/esrrhs/gohome v0.0.0-20250426022937-504d912bccf7 6 | 7 | require ( 8 | github.com/OneOfOne/xxhash v1.2.8 // indirect 9 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect 10 | github.com/golang/protobuf v1.5.4 // indirect 11 | github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect 12 | github.com/google/uuid v1.6.0 // indirect 13 | github.com/klauspost/cpuid/v2 v2.2.10 // indirect 14 | github.com/klauspost/reedsolomon v1.12.4 // indirect 15 | github.com/onsi/ginkgo/v2 v2.23.4 // indirect 16 | github.com/pkg/errors v0.9.1 // indirect 17 | github.com/quic-go/quic-go v0.51.0 // indirect 18 | github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect 19 | github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect 20 | github.com/tjfoc/gmsm v1.4.1 // indirect 21 | github.com/xtaci/kcp-go v5.4.20+incompatible // indirect 22 | github.com/xtaci/smux v1.5.34 // indirect 23 | go.uber.org/automaxprocs v1.6.0 // indirect 24 | go.uber.org/mock v0.5.1 // indirect 25 | golang.org/x/crypto v0.37.0 // indirect 26 | golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect 27 | golang.org/x/mod v0.24.0 // indirect 28 | golang.org/x/net v0.39.0 // indirect 29 | golang.org/x/sync v0.13.0 // indirect 30 | golang.org/x/sys v0.32.0 // indirect 31 | golang.org/x/tools v0.32.0 // indirect 32 | google.golang.org/protobuf v1.36.6 // indirect 33 | ) 34 | -------------------------------------------------------------------------------- /helper_linux.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "syscall" 7 | ) 8 | 9 | const ( 10 | SO_ORIGINAL_DST = 80 11 | ) 12 | 13 | func getOriginalDst(clientConn *net.TCPConn) (host string, port int, err error) { 14 | 15 | file, err := clientConn.File() 16 | if err != nil { 17 | return "", 0, err 18 | } 19 | defer file.Close() 20 | fd := file.Fd() 21 | 22 | addr, err := 23 | syscall.GetsockoptIPv6Mreq( 24 | int(fd), 25 | syscall.IPPROTO_IP, 26 | SO_ORIGINAL_DST) 27 | if err != nil { 28 | return "", 0, err 29 | } 30 | 31 | rawaddr := make([]byte, 0) 32 | 33 | // \attention: IPv4 only!!! 34 | // address type, 1 - IPv4, 4 - IPv6, 3 - hostname, only IPv4 is supported now 35 | rawaddr = append(rawaddr, byte(1)) 36 | // raw IP address, 4 bytes for IPv4 or 16 bytes for IPv6, only IPv4 is supported now 37 | rawaddr = append(rawaddr, addr.Multiaddr[4]) 38 | rawaddr = append(rawaddr, addr.Multiaddr[5]) 39 | rawaddr = append(rawaddr, addr.Multiaddr[6]) 40 | rawaddr = append(rawaddr, addr.Multiaddr[7]) 41 | // port 42 | rawaddr = append(rawaddr, addr.Multiaddr[2]) 43 | rawaddr = append(rawaddr, addr.Multiaddr[3]) 44 | 45 | host = fmt.Sprintf("%d.%d.%d.%d", 46 | addr.Multiaddr[4], 47 | addr.Multiaddr[5], 48 | addr.Multiaddr[6], 49 | addr.Multiaddr[7]) 50 | 51 | port = int(uint16(addr.Multiaddr[2])<<8 + uint16(addr.Multiaddr[3])) 52 | 53 | return 54 | } 55 | -------------------------------------------------------------------------------- /helper_windows.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net" 5 | ) 6 | 7 | // just test 8 | func getOriginalDst(clientConn *net.TCPConn) (host string, port int, err error) { 9 | host = "39.106.101.133" 10 | port = 22 11 | err = nil 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/esrrhs/gohome/common" 6 | "github.com/esrrhs/gohome/loggo" 7 | "github.com/esrrhs/gohome/network" 8 | "io" 9 | "net" 10 | ) 11 | 12 | func main() { 13 | 14 | defer common.CrashLog() 15 | 16 | listen := flag.String("l", "", "listen addr") 17 | target := flag.String("t", "", "target addr") 18 | nolog := flag.Int("nolog", 0, "write log file") 19 | noprint := flag.Int("noprint", 0, "print stdout") 20 | loglevel := flag.String("loglevel", "info", "log level") 21 | 22 | flag.Parse() 23 | 24 | if *listen == "" || *target == "" { 25 | flag.Usage() 26 | return 27 | } 28 | 29 | level := loggo.LEVEL_INFO 30 | if loggo.NameToLevel(*loglevel) >= 0 { 31 | level = loggo.NameToLevel(*loglevel) 32 | } 33 | loggo.Ini(loggo.Config{ 34 | Level: level, 35 | Prefix: "yellowsocks", 36 | MaxDay: 3, 37 | NoLogFile: *nolog > 0, 38 | NoPrint: *noprint > 0, 39 | }) 40 | loggo.Info("start...") 41 | 42 | tcpaddr, err := net.ResolveTCPAddr("tcp", *listen) 43 | if err != nil { 44 | loggo.Error("listen fail %s", err) 45 | return 46 | } 47 | 48 | tcplistenConn, err := net.ListenTCP("tcp", tcpaddr) 49 | if err != nil { 50 | loggo.Error("Error listening for tcp packets: %s", err) 51 | return 52 | } 53 | loggo.Info("listen ok %s", tcpaddr.String()) 54 | 55 | dstaddr, err := net.ResolveTCPAddr("tcp", *target) 56 | if err != nil { 57 | loggo.Error("target fail %s", err) 58 | return 59 | } 60 | loggo.Info("target %s", dstaddr.String()) 61 | 62 | for { 63 | conn, err := tcplistenConn.AcceptTCP() 64 | if err != nil { 65 | loggo.Info("Error accept tcp %s", err) 66 | continue 67 | } 68 | 69 | go process(conn, dstaddr) 70 | } 71 | } 72 | 73 | func process(conn *net.TCPConn, socks5addr *net.TCPAddr) { 74 | 75 | defer common.CrashLog() 76 | 77 | loggo.Info("start conn from %s", conn.RemoteAddr()) 78 | 79 | host, port, err := getOriginalDst(conn) 80 | 81 | loggo.Info("parse conn from %s -> %s:%d", conn.RemoteAddr(), host, port) 82 | 83 | socks5conn, err := net.DialTCP("tcp", nil, socks5addr) 84 | if err != nil { 85 | loggo.Info("dial socks5 conn fail %s %v", socks5addr, err) 86 | return 87 | } 88 | 89 | loggo.Info("dial socks5 conn ok %s -> %s:%d", conn.RemoteAddr(), host, port) 90 | 91 | err = network.Sock5Handshake(socks5conn, 0, "", "") 92 | if err != nil { 93 | loggo.Error("sock5Handshake fail %s", err) 94 | return 95 | } 96 | 97 | loggo.Info("Handshake socks5 conn ok %s -> %s:%d", conn.RemoteAddr(), host, port) 98 | 99 | err = network.Sock5SetRequest(socks5conn, host, port, 0) 100 | if err != nil { 101 | conn.Close() 102 | loggo.Error("sock5SetRequest fail %s", err) 103 | return 104 | } 105 | 106 | loggo.Info("SetRequest socks5 conn ok %s -> %s:%d", conn.RemoteAddr(), host, port) 107 | 108 | go transfer(conn, socks5conn, conn.RemoteAddr().String(), socks5conn.RemoteAddr().String()) 109 | go transfer(socks5conn, conn, socks5conn.RemoteAddr().String(), conn.RemoteAddr().String()) 110 | 111 | loggo.Info("process conn ok %s -> %s:%d", conn.RemoteAddr(), host, port) 112 | } 113 | 114 | func transfer(destination io.WriteCloser, source io.ReadCloser, dst string, src string) { 115 | defer common.CrashLog() 116 | defer destination.Close() 117 | defer source.Close() 118 | loggo.Info("begin transfer from %s -> %s", src, dst) 119 | io.Copy(destination, source) 120 | loggo.Info("end transfer from %s -> %s", src, dst) 121 | } 122 | -------------------------------------------------------------------------------- /pack.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #set -x 3 | NAME="yellowsocks" 4 | 5 | export GO111MODULE=off 6 | 7 | #go tool dist list 8 | build_list=$(go tool dist list) 9 | 10 | rm pack -rf 11 | rm pack.zip -f 12 | mkdir pack 13 | 14 | for line in $build_list; do 15 | os=$(echo "$line" | awk -F"/" '{print $1}') 16 | arch=$(echo "$line" | awk -F"/" '{print $2}') 17 | echo "os="$os" arch="$arch" start build" 18 | if [ $os != "linux" ] && [ $os != "windows" ]; then 19 | continue 20 | fi 21 | CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags="-s -w" 22 | if [ $? -ne 0 ]; then 23 | echo "os="$os" arch="$arch" build fail" 24 | exit 1 25 | fi 26 | if [ $os = "windows" ]; then 27 | zip ${NAME}_"${os}"_"${arch}"".zip" $NAME".exe" 28 | if [ $? -ne 0 ]; then 29 | echo "os="$os" arch="$arch" zip fail" 30 | exit 1 31 | fi 32 | mv ${NAME}_"${os}"_"${arch}"".zip" pack/ 33 | rm $NAME".exe" -f 34 | else 35 | zip ${NAME}_"${os}"_"${arch}"".zip" $NAME 36 | if [ $? -ne 0 ]; then 37 | echo "os="$os" arch="$arch" zip fail" 38 | exit 1 39 | fi 40 | mv ${NAME}_"${os}"_"${arch}"".zip" pack/ 41 | rm $NAME -f 42 | fi 43 | echo "os="$os" arch="$arch" done build" 44 | done 45 | 46 | zip pack.zip pack/ -r 47 | 48 | echo "all done" 49 | --------------------------------------------------------------------------------