mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-12-24 13:27:56 +08:00
添加macos的tproxy功能; 修订文档,代码,示例
将条件编译相关文件全部移动到 cmd/verysimple文件夹中
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
# 免责声明与鸣谢
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//go:build !noquic
|
||||
|
||||
package v2ray_simple
|
||||
package main
|
||||
|
||||
import _ "github.com/e1732a364fed/v2ray_simple/advLayer/quic"
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
package v2ray_simple
|
||||
//go:build linux || darwin
|
||||
// +build linux darwin
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "github.com/e1732a364fed/v2ray_simple/proxy/tproxy"
|
||||
@@ -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功能,可以指定 某一个网卡 来监听 或者拨号.
|
||||
|
||||
@@ -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" ]
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
10
netLayer/tproxy/route_darwin.go
Normal file
10
netLayer/tproxy/route_darwin.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package tproxy
|
||||
|
||||
import "github.com/e1732a364fed/v2ray_simple/utils"
|
||||
|
||||
func SetRouteByPort(port int) error {
|
||||
return utils.ErrUnImplemented
|
||||
}
|
||||
|
||||
func CleanupRoutes() {
|
||||
}
|
||||
@@ -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
|
||||
52
netLayer/tproxy/tproxy_darwin.go
Normal file
52
netLayer/tproxy/tproxy_darwin.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
143
proxy/tproxy/server_darwin.go
Normal file
143
proxy/tproxy/server_darwin.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user