添加macos的tproxy功能; 修订文档,代码,示例

将条件编译相关文件全部移动到 cmd/verysimple文件夹中
This commit is contained in:
e1732a364fed
2000-01-01 00:00:00 +00:00
parent 55088eb268
commit 335d864e53
13 changed files with 288 additions and 32 deletions

View File

@@ -11,12 +11,6 @@
[9]: https://img.shields.io/github/release/e1732a364fed/v2ray_simple/all.svg?style=flat-square
[10]: https://img.shields.io/github/go-mod/go-version/e1732a364fed/v2ray_simple?style=flat-square
### 最新消息
最近在开发vsb计划即verysimple 面板项目,在 https://github.com/e1732a364fed/vsb , 可以看看。
telegram channel: https://t.me/+r5hKQKYyeuowMTcx
# verysimple
verysimple 实际上 谐音来自 V2ray Simple (显然只适用于汉语母语者), 意思就是极简.
@@ -659,6 +653,8 @@ verysimple 版本 v1.0.3
如果本作作者突然停更,这里允许任何人以 verysimple 作者的名义fork并 接盘。你只要声称自己是原作者忘记了github和自己邮箱的密码只好重开这不就ok了。
telegram channel: https://t.me/+r5hKQKYyeuowMTcx
# 免责声明与鸣谢

View File

@@ -1,6 +1,6 @@
//go:build !noquic
package v2ray_simple
package main
import _ "github.com/e1732a364fed/v2ray_simple/advLayer/quic"

View File

@@ -297,9 +297,7 @@ func setupUI() {
}
filesM.AppendItem("Open github").OnClicked(openUrlFunc(weblink))
filesM.AppendItem("Check github releases").OnClicked(openUrlFunc(weblink + "releases"))
qi := filesM.AppendItem("Quit App")
qi.OnClicked(func(mi *ui.MenuItem, w *ui.Window) {
filesM.AppendItem("Quit App").OnClicked(func(mi *ui.MenuItem, w *ui.Window) {
syscall.Kill(syscall.Getpid(), syscall.SIGINT) //退出app
})

View File

@@ -1,4 +1,7 @@
package v2ray_simple
//go:build linux || darwin
// +build linux darwin
package main
import (
_ "github.com/e1732a364fed/v2ray_simple/proxy/tproxy"

View File

@@ -5,7 +5,10 @@
# 我们不像v2ray/xray一样 使用 dokodemo的额外配置来处理tproxy而是单独使用 tproxy作为一个协议.
# tproxy只支持客户端, 且目前只支持linux系统, 一般用于软路由/树莓派等.
# tproxy只支持客户端, 且目前只支持linux/macos系统
# 在macos中不需要额外参数但是仅支持ipv4和tcp。而且macos 12/13 系统上的 pfctl有bug无法配置路由.
# 建议mac上还是用tun的方法.
[[listen]]
protocol = "tproxy"
@@ -16,8 +19,12 @@ port = 12345
# 不用指明network, 只要指明了tproxy, 就会同时自动监听 tcp和 dup.
# 但是如果你指明了 network = tcp, 就不会转发udp, 你指明了 network = udp 的话,就不会转发 tcp.
#extra = { auto_iptables = true } # 如果给出了 auto_iptables, 且 【protocol 为 tproxy 的listen】【只有一个】, 则程序会自动在开始监听前配置好iptables并在程序退出前 清除iptables中被程序改动的部分。
# auto_iptables 会一字不差地运行 上面 给出的 toutyrater 的教程中的 iptables命令。
#extra = { auto_iptables = true }
# 如果给出了 auto_iptables, 且 【protocol 为 tproxy 的listen】【只有一个】,
# 则程序会自动配置路由表
# linux上程序会自动在开始监听前配置好iptables并在程序退出前 清除iptables中被程序改动的部分。
# linux上, auto_iptables 会一字不差地运行 上面 给出的 toutyrater 的教程中的 iptables命令。
[[dial]]
@@ -29,6 +36,6 @@ version = 0
insecure = true
utls = true
sockopt.mark = 255 #根据上面 toutyrater 给出的 iptables命令我们这里要设置 mark为255. 看一下 上面 toutyrater的教程吧
sockopt.mark = 255 #linux 上, 根据上面 toutyrater 给出的 iptables命令我们这里要设置 mark为255. 看一下 上面 toutyrater的教程吧
# sockopt.device = "eth32423" # vs还支持 bindToDevice功能可以指定 某一个网卡 来监听 或者拨号.

View File

@@ -39,6 +39,7 @@ protocol = "tun"
# 如果 extra.tun_auto_route 给出vs_gui会试图自动配置路由表.
# 此时必须额外给出需要 直连的ip列表, 比如你的 代理服务器的ip地址; 如果不给出, 则不会自动配置路由表
# 目前的自动配置逻辑 完全仿照上面的路由示例。
extra.tun_auto_route = true
extra.tun_auto_route_direct_list = [ "127.0.0.1" ]

View File

@@ -1,7 +1,11 @@
/*
Package tproxy listens tproxy and setup corresponding iptables for linux.
透明代理只能用于linux
vs的透明代理只能用于linux 和 macos
原理上透明代理与tun/tap不同透明代理直接工作在传输层第四层tcp/udp上无需解析ip包
下面的文档首先探讨了linux的tproxy
# About TProxy 关于透明代理
@@ -96,6 +100,35 @@ https://toutyrater.github.io/app/tproxy.html
透明代理与Redir的参考博客
http://ivo-wang.github.io/2018/02/24/ss-redir/
# 关于mac上的实现
我参考了如下内容
https://penglei.github.io/post/transparent_proxy_on_macosx/
https://docs.mitmproxy.org/stable/howto-transparent/#macos
https://github.com/Dreamacro/clash/issues/745
https://github.com/shadowsocks/go-shadowsocks2
mac上的透明代理使用了pf命令通过一条命令来将全局流量导向特定端口
不过只支持ipv4和tcp
sudo sysctl -w net.inet.ip.forwarding=1
创建一个文件pf.conf
rdr pass on en0 inet proto tcp to any port {80, 443} -> 127.0.0.1 port 8080
sudo pfctl -f pf.conf
sudo pfctl -e
查看效果
sudo pfctl -s nat
如果想停止使用透明代理访问禁用pf(sudo pfctl -d)或者清空pf规则(sudo pfctl -F all)即可。
我试了mac12.0 mac13 的 pfctl有问题设路由不好使。参考
https://github.com/mitmproxy/mitmproxy/issues/4835
*/
package tproxy
@@ -155,7 +188,7 @@ func (m *Machine) Init() {
func (m *Machine) SetIPTable(port int) {
if port > 0 {
SetIPTablesByPort(port)
SetRouteByPort(port)
m.iptablePort = port
}
@@ -175,19 +208,19 @@ func (m *Machine) Stop() {
tCh := time.After(time.Second)
select {
case <-tCh:
log.Println("close tproxy listener timeout")
log.Println("tproxy close listener timeout")
case <-ch:
break
}
}
if m.UDPConn != nil {
log.Println("closing tproxy udp conn")
log.Println("tproxy closing udp conn")
m.UDPConn.Close()
}
if m.iptablePort > 0 {
CleanupIPTables()
CleanupRoutes()
}
}

View File

@@ -1,14 +1,14 @@
//go:build !linux
//go:build !(linux || darwin)
package tproxy
import "github.com/e1732a364fed/v2ray_simple/utils"
//placeholder for non-linux systems, return utils.ErrNotImplemented
func SetIPTablesByPort(port int) error {
// placeholder for unsupported systems, return utils.ErrNotImplemented
func SetRouteByPort(port int) error {
return utils.ErrUnImplemented
}
//placeholder for non-linux systems
func CleanupIPTables() {
// placeholder for unsupported systems
func CleanupRoutes() {
}

View File

@@ -0,0 +1,10 @@
package tproxy
import "github.com/e1732a364fed/v2ray_simple/utils"
func SetRouteByPort(port int) error {
return utils.ErrUnImplemented
}
func CleanupRoutes() {
}

View File

@@ -9,6 +9,8 @@ import (
"go.uber.org/zap"
)
//配置iptables
func execCmd(cmdStr string) (err error) {
utils.ZapLogger.Info("tproxy run cmd", zap.String("cmd", cmdStr))
@@ -81,8 +83,8 @@ iptables -t mangle -X V2RAY_MASK`
var lastPortSet int
//commands from https://toutyrater.github.io/app/tproxy.html
func SetIPTablesByPort(port int) error {
// commands from https://toutyrater.github.io/app/tproxy.html
func SetRouteByPort(port int) error {
cmd1 := exec.Command("iptables", "-V")
if err := cmd1.Run(); err != nil {
@@ -93,19 +95,19 @@ func SetIPTablesByPort(port int) error {
return execCmdList(fmt.Sprintf(toutyRaterIptableCmdList, port, port))
}
//port 12345
// port 12345
func SetIPTablesByDefault() error {
return SetIPTablesByPort(12345)
return SetRouteByPort(12345)
}
//port 12345
// port 12345
func CleanupIPTablesByDefault() {
execCmdList(fmt.Sprintf(iptableRMCmdList, 12345, 12345))
}
//clear iptables set by the last SetIPTablesByPort call
func CleanupIPTables() {
// clear iptables set by the last SetRouteByPort call
func CleanupRoutes() {
if lastPortSet != 0 {
execCmdList(fmt.Sprintf(iptableRMCmdList, lastPortSet, lastPortSet))
lastPortSet = 0

View File

@@ -0,0 +1,52 @@
package tproxy
import (
"net"
"syscall"
"unsafe"
)
// https://github.com/shadowsocks/go-shadowsocks2/blob/master/pfutil/pf_darwin.go
func HandshakeTCP(c *net.TCPConn) (*net.TCPAddr, error) {
const (
PF_INOUT = 0
PF_IN = 1
PF_OUT = 2
IOC_OUT = 0x40000000
IOC_IN = 0x80000000
IOC_INOUT = IOC_IN | IOC_OUT
IOCPARM_MASK = 0x1FFF
LEN = 4*16 + 4*4 + 4*1
// #define _IOC(inout,group,num,len) (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num))
// #define _IOWR(g,n,t) _IOC(IOC_INOUT, (g), (n), sizeof(t))
// #define DIOCNATLOOK _IOWR('D', 23, struct pfioc_natlook)
DIOCNATLOOK = IOC_INOUT | ((LEN & IOCPARM_MASK) << 16) | ('D' << 8) | 23
)
fd, err := syscall.Open("/dev/pf", 0, syscall.O_RDONLY)
if err != nil {
return nil, err
}
defer syscall.Close(fd)
nl := struct { // struct pfioc_natlook
saddr, daddr, rsaddr, rdaddr [16]byte
sxport, dxport, rsxport, rdxport [4]byte
af, proto, protoVariant, direction uint8
}{
af: syscall.AF_INET,
proto: syscall.IPPROTO_TCP,
direction: PF_OUT,
}
saddr := c.RemoteAddr().(*net.TCPAddr)
daddr := c.LocalAddr().(*net.TCPAddr)
copy(nl.saddr[:], saddr.IP)
copy(nl.daddr[:], daddr.IP)
nl.sxport[0], nl.sxport[1] = byte(saddr.Port>>8), byte(saddr.Port)
nl.dxport[0], nl.dxport[1] = byte(daddr.Port>>8), byte(daddr.Port)
if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), DIOCNATLOOK, uintptr(unsafe.Pointer(&nl))); errno != 0 {
return nil, errno
}
var addr net.TCPAddr
addr.IP = nl.rdaddr[:4]
addr.Port = int(nl.rdxport[0])<<8 | int(nl.rdxport[1])
return &addr, nil
}

View File

@@ -1,3 +1,13 @@
/*
Packages tun provides utilities for tun.
tun 工作在第三层 IP层上。
我们监听tun从中提取出 tcp/udp 流。
我们使用 github.com/eycorsican/go-tun2socks 包
这个包在windows上会使用tap。
*/
package tun
import (
@@ -13,6 +23,7 @@ import (
"go.uber.org/zap"
)
// implements netLayer.MsgConn
type coreUDPConnAdapter struct {
core.UDPConn
netLayer.EasyDeadline

View File

@@ -0,0 +1,143 @@
// Package tproxy implements proxy.Server for tproxy.
package tproxy
import (
"io"
"net"
"net/url"
"sync"
"github.com/e1732a364fed/v2ray_simple/netLayer"
"github.com/e1732a364fed/v2ray_simple/netLayer/tproxy"
"github.com/e1732a364fed/v2ray_simple/proxy"
"github.com/e1732a364fed/v2ray_simple/utils"
"go.uber.org/zap"
)
//在darwin上代码相当于linux上的精简版本, 不提供udp支持
const name = "tproxy"
func init() {
proxy.RegisterServer(name, &ServerCreator{})
}
type ServerCreator struct{ proxy.CreatorCommonStruct }
func (ServerCreator) URLToListenConf(url *url.URL, lc *proxy.ListenConf, format int) (*proxy.ListenConf, error) {
if lc == nil {
return nil, utils.ErrNilParameter
}
return lc, nil
}
func (ServerCreator) NewServer(lc *proxy.ListenConf) (proxy.Server, error) {
s := &Server{}
if thing := lc.Extra["auto_iptables"]; thing != nil {
if auto, ok := utils.AnyToBool(thing); ok && auto {
s.shouldSetRoute = true
}
}
return s, nil
}
func (ServerCreator) AfterCommonConfServer(ps proxy.Server) (err error) {
s := ps.(*Server)
if s.shouldSetRoute {
err = tproxy.SetRouteByPort(s.ListenConf.Port)
}
return
}
// implements proxy.ListenerServer
type Server struct {
proxy.Base
shouldSetRoute bool
infoChan chan<- netLayer.TCPRequestInfo
tm *tproxy.Machine
sync.Once
}
func NewServer() (proxy.Server, error) {
d := &Server{}
return d, nil
}
func (*Server) Name() string { return name }
func (s *Server) SelfListen() (is, tcp, udp bool) {
return true, true, true //darwin的tproxy不支持udp但是如果传入false则v2ray_simple包会试图自行监听udp。
//所以暂时的解决方案是欺骗它告诉他我们都监听
}
func (s *Server) Close() error {
s.Stop()
return nil
}
func (s *Server) Stop() {
s.Once.Do(func() {
s.tm.Stop()
if s.infoChan != nil {
close(s.infoChan)
}
})
}
func (s *Server) StartListen(infoChan chan<- netLayer.TCPRequestInfo, udpInfoChan chan<- netLayer.UDPRequestInfo) io.Closer {
tm := new(tproxy.Machine)
_, lt, _ := s.SelfListen()
if lt {
s.infoChan = infoChan
lis, err := netLayer.ListenAndAccept("tcp", s.Addr, s.Sockopt, 0, func(conn net.Conn) {
tcpconn := conn.(*net.TCPConn)
ta, err := tproxy.HandshakeTCP(tcpconn)
if err != nil {
if ce := utils.CanLogErr("tproxy HandshakeTCP failed"); ce != nil {
ce.Write(zap.Error(err))
}
return
}
targetAddr := netLayer.NewAddrFromTCPAddr(ta)
info := netLayer.TCPRequestInfo{
Conn: tcpconn,
Target: targetAddr,
}
if ce := utils.CanLogInfo("TProxy got new tcp"); ce != nil {
ce.Write(zap.String("->", targetAddr.String()))
}
if tm.Closed() {
return
}
infoChan <- info
})
if err != nil {
if ce := utils.CanLogErr("TProxy listen tcp failed"); ce != nil {
ce.Write(zap.Error(err))
}
}
tm.Listener = lis
}
tm.Init()
s.tm = tm
return s
}
func (s *Server) Handshake(underlay net.Conn) (net.Conn, netLayer.MsgConn, netLayer.Addr, error) {
return nil, nil, netLayer.Addr{}, utils.ErrUnImplemented
}