├── .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 | [](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 |
--------------------------------------------------------------------------------