mirror of
https://github.com/e1732a364fed/v2ray_simple.git
synced 2025-10-20 15:35:57 +08:00
添加自定义网络层功能;添加udp网络层支持
配置中,listen和dial中,可添加 network = "udp" 字段,不添加则默认tcp
This commit is contained in:
37
config.go
37
config.go
@@ -1,40 +1,3 @@
|
|||||||
/*
|
|
||||||
Package main 读取配置文件,将其内容转化为 proxy.Client和 proxy.Server,然后进行代理转发.
|
|
||||||
|
|
||||||
命令行参数请使用 --help查看详情。
|
|
||||||
|
|
||||||
如果一个命令行参数无法在标准配置中进行配置,那么它就属于高级选项,或者不推荐的选项,或者正在开发中的功能.
|
|
||||||
|
|
||||||
Config Format 配置格式
|
|
||||||
|
|
||||||
一共有三种配置格式,极简模式,标准模式,兼容模式。
|
|
||||||
|
|
||||||
“极简模式”(即 verysimple mode),入口和出口仅有一个,而且都是使用共享链接的url格式来配置.
|
|
||||||
|
|
||||||
标准模式使用toml格式。
|
|
||||||
|
|
||||||
兼容模式可以兼容v2ray现有json格式。(暂未实现)。
|
|
||||||
|
|
||||||
极简模式的理念是,配置文件的字符尽量少,尽量短小精悍;
|
|
||||||
|
|
||||||
还有个命令行模式,就是直接把极简模式的url 放到命令行参数中,比如:
|
|
||||||
|
|
||||||
verysimple -L socks5://sfdfsaf -D direct://
|
|
||||||
|
|
||||||
|
|
||||||
Structure 本项目结构
|
|
||||||
|
|
||||||
main -> proxy.Standard(配置文件) -> netLayer-> tlsLayer -> httpLayer -> ws -> proxy.
|
|
||||||
|
|
||||||
main中,读取配置文件,生成 Dail、Listen 和 RoutePolicy 对象后,开始监听;
|
|
||||||
|
|
||||||
用 netLayer操纵路由,用tlsLayer嗅探tls,用httpLayer操纵回落,可选经过ws, 然后都搞好后,传到proxy,然后就开始转发
|
|
||||||
|
|
||||||
Other - 其他
|
|
||||||
|
|
||||||
另外,本作暂时不考虑引入外界log包。依赖越少越好。
|
|
||||||
|
|
||||||
*/
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
88
main.go
88
main.go
@@ -9,9 +9,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/hahahrfool/v2ray_simple/httpLayer"
|
"github.com/hahahrfool/v2ray_simple/httpLayer"
|
||||||
"github.com/hahahrfool/v2ray_simple/netLayer"
|
"github.com/hahahrfool/v2ray_simple/netLayer"
|
||||||
@@ -35,8 +33,6 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
desc = "v2ray_simple, a very simple implementation of V2Ray, 并且在某些地方试图走在v2ray前面"
|
|
||||||
|
|
||||||
configFileName string
|
configFileName string
|
||||||
|
|
||||||
uniqueTestDomain string //有时需要测试到单一网站的流量,此时为了避免其它干扰,需要在这里声明 一下 该域名,然后程序里会进行过滤
|
uniqueTestDomain string //有时需要测试到单一网站的流量,此时为了避免其它干扰,需要在这里声明 一下 该域名,然后程序里会进行过滤
|
||||||
@@ -55,17 +51,17 @@ var (
|
|||||||
serversTagMap = make(map[string]proxy.Server)
|
serversTagMap = make(map[string]proxy.Server)
|
||||||
clientsTagMap = make(map[string]proxy.Client)
|
clientsTagMap = make(map[string]proxy.Client)
|
||||||
|
|
||||||
|
listenURL string
|
||||||
|
dialURL string
|
||||||
|
|
||||||
tls_lazy_encrypt bool
|
tls_lazy_encrypt bool
|
||||||
tls_lazy_secure bool
|
tls_lazy_secure bool
|
||||||
|
|
||||||
routePolicy *netLayer.RoutePolicy
|
routePolicy *netLayer.RoutePolicy
|
||||||
|
|
||||||
listenURL string
|
|
||||||
dialURL string
|
|
||||||
|
|
||||||
isServerEnd bool
|
|
||||||
|
|
||||||
mainFallback *httpLayer.ClassicFallback
|
mainFallback *httpLayer.ClassicFallback
|
||||||
|
|
||||||
|
isServerEnd bool //这个是代码里推断的,不一定准确;不过目前仅被用于tls lazy encrypt,所以不是很重要
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -206,7 +202,9 @@ func main() {
|
|||||||
if confMode == simpleMode {
|
if confMode == simpleMode {
|
||||||
go listenSer(nil, defaultInServer)
|
go listenSer(nil, defaultInServer)
|
||||||
} else {
|
} else {
|
||||||
go listenAllServers()
|
for _, inServer := range allServers {
|
||||||
|
go listenSer(nil, inServer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -216,60 +214,27 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenAllServers() {
|
|
||||||
|
|
||||||
for _, inServer := range allServers {
|
|
||||||
|
|
||||||
listener, err := net.Listen("tcp", inServer.AddrStr())
|
|
||||||
defer inServer.Stop()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalln("can not listen inServer on", inServer.AddrStr(), err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if utils.CanLogInfo() {
|
|
||||||
log.Println(proxy.GetFullName(inServer), "is listening TCP on ", inServer.AddrStr())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
go listenSer(listener, inServer)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func listenSer(listener net.Listener, inServer proxy.Server) {
|
func listenSer(listener net.Listener, inServer proxy.Server) {
|
||||||
|
|
||||||
var err error
|
theFunc := func(conn net.Conn) {
|
||||||
if listener == nil {
|
handleNewIncomeConnection(inServer, conn)
|
||||||
listener, err = net.Listen("tcp", inServer.AddrStr())
|
}
|
||||||
|
|
||||||
|
network := inServer.Network()
|
||||||
|
err := netLayer.ListenAndAccept(network, inServer.AddrStr(), theFunc)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
if utils.CanLogInfo() {
|
||||||
|
log.Println(proxy.GetFullName(inServer), "is listening ", network, "on", inServer.AddrStr())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("can not listen inServer on", inServer.AddrStr(), err)
|
log.Fatalln("can not listen inServer on", inServer.AddrStr(), err)
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
|
||||||
lc, err := listener.Accept()
|
|
||||||
if err != nil {
|
|
||||||
errStr := err.Error()
|
|
||||||
if strings.Contains(errStr, "closed") {
|
|
||||||
log.Println("local connection closed", err)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
log.Println("failed to accepted connection: ", err)
|
|
||||||
if strings.Contains(errStr, "too many") {
|
|
||||||
log.Println("To many incoming conn! Sleep ", errStr)
|
|
||||||
time.Sleep(time.Millisecond * 500)
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go handleNewIncomeConnection(inServer, lc)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstance net.Conn) {
|
func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstance net.Conn) {
|
||||||
@@ -325,6 +290,8 @@ func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstanc
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//log.Println("handshake passed tls")
|
||||||
|
|
||||||
if adv := inServer.AdvancedLayer(); adv != "" {
|
if adv := inServer.AdvancedLayer(); adv != "" {
|
||||||
if adv == "ws" {
|
if adv == "ws" {
|
||||||
wsConn, err := ws.Handshake(inServer.GetListenConf().Path, thisLocalConnectionInstance)
|
wsConn, err := ws.Handshake(inServer.GetListenConf().Path, thisLocalConnectionInstance)
|
||||||
@@ -463,7 +430,7 @@ afterLocalServerHandshake:
|
|||||||
}
|
}
|
||||||
|
|
||||||
//如果目标是udp则要分情况讨论
|
//如果目标是udp则要分情况讨论
|
||||||
if targetAddr.IsUDP {
|
if targetAddr.Network == "udp" {
|
||||||
|
|
||||||
switch inServer.Name() {
|
switch inServer.Name() {
|
||||||
case "vlesss":
|
case "vlesss":
|
||||||
@@ -535,7 +502,7 @@ afterLocalServerHandshake:
|
|||||||
// 因为direct使用 proxy.RelayUDP_to_Direct 函数 直接实现了fullcone
|
// 因为direct使用 proxy.RelayUDP_to_Direct 函数 直接实现了fullcone
|
||||||
// 那么我们只需要传入一个 UDP_Extractor 即可
|
// 那么我们只需要传入一个 UDP_Extractor 即可
|
||||||
|
|
||||||
if targetAddr.IsUDP {
|
if targetAddr.Network == "udp" {
|
||||||
|
|
||||||
var unknownRemoteAddrMsgWriter netLayer.UDPResponseWriter
|
var unknownRemoteAddrMsgWriter netLayer.UDPResponseWriter
|
||||||
|
|
||||||
@@ -587,12 +554,13 @@ afterLocalServerHandshake:
|
|||||||
//log.Println("will dial", client.AddrStr())
|
//log.Println("will dial", client.AddrStr())
|
||||||
|
|
||||||
realTargetAddr, _ = netLayer.NewAddr(client.AddrStr())
|
realTargetAddr, _ = netLayer.NewAddr(client.AddrStr())
|
||||||
|
realTargetAddr.Network = client.Network()
|
||||||
}
|
}
|
||||||
|
|
||||||
clientConn, err := realTargetAddr.Dial()
|
clientConn, err := realTargetAddr.Dial()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if utils.CanLogErr() {
|
if utils.CanLogErr() {
|
||||||
log.Println("failed in dial", targetAddr.String(), ", Reason: ", err)
|
log.Println("failed in dial", realTargetAddr.String(), ", Reason: ", err)
|
||||||
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Atyp, for vless and vmess; 注意与 trojan和socks5的区别,trojan和socks5的相同含义的值是1,3,4
|
// Atyp, for vless and vmess; 注意与 trojan和socks5的区别,trojan和socks5的相同含义的值是1,3,4
|
||||||
@@ -15,17 +16,20 @@ const (
|
|||||||
|
|
||||||
// Addr represents a address that you want to access by proxy. Either Name or IP is used exclusively.
|
// Addr represents a address that you want to access by proxy. Either Name or IP is used exclusively.
|
||||||
type Addr struct {
|
type Addr struct {
|
||||||
Name string // domain name
|
Name string // domain name, 或者 unix domain socket 的 文件路径
|
||||||
IP net.IP
|
IP net.IP
|
||||||
Port int
|
Port int
|
||||||
IsUDP bool
|
//IsUDP bool
|
||||||
|
|
||||||
|
Network string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAddrFromUDPAddr(addr *net.UDPAddr) *Addr {
|
func NewAddrFromUDPAddr(addr *net.UDPAddr) *Addr {
|
||||||
return &Addr{
|
return &Addr{
|
||||||
IP: addr.IP,
|
IP: addr.IP,
|
||||||
Port: addr.Port,
|
Port: addr.Port,
|
||||||
IsUDP: true,
|
//IsUDP: true,
|
||||||
|
Network: "udp",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +78,12 @@ func NewAddrByURL(addrStr string) (*Addr, error) {
|
|||||||
a.Name = host
|
a.Name = host
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.Network = u.Scheme
|
||||||
|
|
||||||
|
/*
|
||||||
if u.Scheme == "udp" {
|
if u.Scheme == "udp" {
|
||||||
a.IsUDP = true
|
a.IsUDP = true
|
||||||
}
|
}*/
|
||||||
|
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
@@ -92,14 +99,12 @@ func (a *Addr) String() string {
|
|||||||
|
|
||||||
func (a *Addr) UrlString() string {
|
func (a *Addr) UrlString() string {
|
||||||
str := a.String()
|
str := a.String()
|
||||||
if a.IsUDP {
|
|
||||||
return "udp://" + str
|
return a.Network + "://" + str
|
||||||
}
|
|
||||||
return "tcp://" + str
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Addr) ToUDPAddr() *net.UDPAddr {
|
func (a *Addr) ToUDPAddr() *net.UDPAddr {
|
||||||
if !a.IsUDP {
|
if a.Network != "udp" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ua, err := net.ResolveUDPAddr("udp", a.String())
|
ua, err := net.ResolveUDPAddr("udp", a.String())
|
||||||
@@ -118,10 +123,19 @@ func (a *Addr) HostStr() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (addr *Addr) Dial() (net.Conn, error) {
|
func (addr *Addr) Dial() (net.Conn, error) {
|
||||||
if addr.IsUDP {
|
//log.Println("Dial called", addr, addr.Network)
|
||||||
return net.Dial("udp", addr.String())
|
|
||||||
}
|
switch addr.Network {
|
||||||
|
case "":
|
||||||
return net.Dial("tcp", addr.String())
|
return net.Dial("tcp", addr.String())
|
||||||
|
|
||||||
|
case "udp":
|
||||||
|
return DialUDP(addr.ToUDPAddr())
|
||||||
|
default:
|
||||||
|
return net.Dial(addr.Network, addr.String())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returned address bytes and type
|
// Returned address bytes and type
|
||||||
@@ -193,3 +207,16 @@ func ParseStrToAddr(s string) (atyp byte, addr []byte, port_uint16 uint16, err e
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func UDPAddr2Byte(addr *net.UDPAddr) [6]byte {
|
||||||
|
ip := addr.IP.To4()
|
||||||
|
|
||||||
|
port := uint16(addr.Port)
|
||||||
|
|
||||||
|
var allByte [6]byte
|
||||||
|
abs := allByte[:]
|
||||||
|
copy(abs, ip)
|
||||||
|
copy(abs[4:], (*(*[2]byte)(unsafe.Pointer(&port)))[:])
|
||||||
|
|
||||||
|
return allByte
|
||||||
|
}
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
package netLayer
|
package netLayer
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// transport Layer
|
// transport Layer, 使用uint16 mask,所以最多支持16种
|
||||||
|
|
||||||
TCP uint16 = 1 << iota
|
TCP uint16 = 1 << iota
|
||||||
UDP
|
UDP
|
||||||
|
UNIX //unix domain socket
|
||||||
Raw_socket
|
Raw_socket
|
||||||
KCP
|
KCP
|
||||||
Quic //quic是一个横跨多个层的协议,这里也算一个,毕竟与kcp类似
|
Quic //quic是一个横跨多个层的协议,这里也算一个,毕竟与kcp类似
|
||||||
@@ -19,6 +20,8 @@ func StrToTransportProtocol(s string) uint16 {
|
|||||||
return TCP
|
return TCP
|
||||||
case "udp":
|
case "udp":
|
||||||
return UDP
|
return UDP
|
||||||
|
case "unix":
|
||||||
|
return UNIX
|
||||||
case "raw":
|
case "raw":
|
||||||
return Raw_socket
|
return Raw_socket
|
||||||
case "kcp":
|
case "kcp":
|
||||||
|
75
netLayer/deadline.go
Normal file
75
netLayer/deadline.go
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package netLayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PipeDeadline is an abstraction for handling timeouts.
|
||||||
|
//copied from golang standard package net
|
||||||
|
type PipeDeadline struct {
|
||||||
|
mu sync.Mutex // Guards timer and cancel
|
||||||
|
timer *time.Timer
|
||||||
|
cancel chan struct{} // Must be non-nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakePipeDeadline() PipeDeadline {
|
||||||
|
return PipeDeadline{cancel: make(chan struct{})}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set sets the point in time when the deadline will time out.
|
||||||
|
// A timeout event is signaled by closing the channel returned by waiter.
|
||||||
|
// Once a timeout has occurred, the deadline can be refreshed by specifying a
|
||||||
|
// t value in the future.
|
||||||
|
//
|
||||||
|
// A zero value for t prevents timeout.
|
||||||
|
func (d *PipeDeadline) Set(t time.Time) {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
|
||||||
|
if d.timer != nil && !d.timer.Stop() {
|
||||||
|
<-d.cancel // Wait for the timer callback to finish and close cancel
|
||||||
|
}
|
||||||
|
d.timer = nil
|
||||||
|
|
||||||
|
// Time is zero, then there is no deadline.
|
||||||
|
closed := isClosedChan(d.cancel)
|
||||||
|
if t.IsZero() {
|
||||||
|
if closed {
|
||||||
|
d.cancel = make(chan struct{})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time in the future, setup a timer to cancel in the future.
|
||||||
|
if dur := time.Until(t); dur > 0 {
|
||||||
|
if closed {
|
||||||
|
d.cancel = make(chan struct{})
|
||||||
|
}
|
||||||
|
d.timer = time.AfterFunc(dur, func() {
|
||||||
|
close(d.cancel)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Time in the past, so close immediately.
|
||||||
|
if !closed {
|
||||||
|
close(d.cancel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait returns a channel that is closed when the deadline is exceeded.
|
||||||
|
func (d *PipeDeadline) Wait() chan struct{} {
|
||||||
|
d.mu.Lock()
|
||||||
|
defer d.mu.Unlock()
|
||||||
|
return d.cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
func isClosedChan(c <-chan struct{}) bool {
|
||||||
|
select {
|
||||||
|
case <-c:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
63
netLayer/listen.go
Normal file
63
netLayer/listen.go
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
package netLayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hahahrfool/v2ray_simple/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func loopAccept(listener net.Listener, acceptFunc func(net.Conn)) {
|
||||||
|
for {
|
||||||
|
newc, err := listener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
errStr := err.Error()
|
||||||
|
if strings.Contains(errStr, "closed") {
|
||||||
|
if utils.CanLogDebug() {
|
||||||
|
log.Println("local connection closed", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if utils.CanLogWarn() {
|
||||||
|
log.Println("failed to accept connection: ", err)
|
||||||
|
}
|
||||||
|
if strings.Contains(errStr, "too many") {
|
||||||
|
if utils.CanLogWarn() {
|
||||||
|
log.Println("To many incoming conn! Sleep ", errStr)
|
||||||
|
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 500)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
go acceptFunc(newc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListenAndAccept 试图监听 所有类型的网络,包括tcp, udp 和 unix domain socket.
|
||||||
|
// 非阻塞,在自己的goroutine中监听.
|
||||||
|
func ListenAndAccept(network, addr string, acceptFunc func(net.Conn)) error {
|
||||||
|
switch network {
|
||||||
|
default:
|
||||||
|
listener, err := net.Listen(network, addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go loopAccept(listener, acceptFunc)
|
||||||
|
case "udp":
|
||||||
|
ua, err := net.ResolveUDPAddr("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
listener, err := NewUDPListener(ua)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
go loopAccept(listener, acceptFunc)
|
||||||
|
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@@ -73,6 +73,11 @@ func (sg *RouteSet) IsTransportProtocolAllowed(p uint16) bool {
|
|||||||
return sg.AllowedTransportLayerProtocols&p > 0
|
return sg.AllowedTransportLayerProtocols&p > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sg *RouteSet) IsAddrNetworkAllowed(a *Addr) bool {
|
||||||
|
p := StrToTransportProtocol(a.Network)
|
||||||
|
return sg.IsTransportProtocolAllowed(p)
|
||||||
|
}
|
||||||
|
|
||||||
func (sg *RouteSet) IsUDPAllowed() bool {
|
func (sg *RouteSet) IsUDPAllowed() bool {
|
||||||
return sg.IsTransportProtocolAllowed(UDP)
|
return sg.IsTransportProtocolAllowed(UDP)
|
||||||
}
|
}
|
||||||
@@ -85,7 +90,7 @@ func (sg *RouteSet) IsAddrIn(a *Addr) bool {
|
|||||||
//我们先过滤传输层,再过滤网络层
|
//我们先过滤传输层,再过滤网络层
|
||||||
|
|
||||||
//目前我们仅支持udp和tcp这两种传输层协议,所以可以如此。以后如果加了更多的话,还需改动
|
//目前我们仅支持udp和tcp这两种传输层协议,所以可以如此。以后如果加了更多的话,还需改动
|
||||||
if a.IsUDP && !sg.IsUDPAllowed() || !a.IsUDP && !sg.IsTCPAllowed() {
|
if !sg.IsAddrNetworkAllowed(a) {
|
||||||
return false
|
return false
|
||||||
|
|
||||||
} else if sg.NetRanger == nil && sg.IPs == nil && sg.Domains == nil && sg.InTags == nil && sg.Countries == nil {
|
} else if sg.NetRanger == nil && sg.IPs == nil && sg.Domains == nil && sg.InTags == nil && sg.Countries == nil {
|
||||||
|
157
netLayer/udpConn.go
Normal file
157
netLayer/udpConn.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package netLayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/hahahrfool/v2ray_simple/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrTimeout = errors.New("timeout")
|
||||||
|
)
|
||||||
|
|
||||||
|
//UDPConn 实现了 net.Conn
|
||||||
|
type UDPConn struct {
|
||||||
|
peerAddr *net.UDPAddr
|
||||||
|
realConn *net.UDPConn
|
||||||
|
|
||||||
|
inMsgChan chan []byte
|
||||||
|
|
||||||
|
readDeadline PipeDeadline
|
||||||
|
writeDeadline PipeDeadline
|
||||||
|
|
||||||
|
unread []byte
|
||||||
|
isClient bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func DialUDP(raddr *net.UDPAddr) (net.Conn, error) {
|
||||||
|
conn, err := net.DialUDP("udp", nil, raddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewUDPConn(raddr, conn, true), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPConn(raddr *net.UDPAddr, conn *net.UDPConn, isClient bool) *UDPConn {
|
||||||
|
inDataChan := make(chan []byte, 50)
|
||||||
|
theUDPConn := &UDPConn{raddr, conn, inDataChan, MakePipeDeadline(),
|
||||||
|
MakePipeDeadline(), []byte{}, isClient}
|
||||||
|
|
||||||
|
//不设置缓存的话,会导致发送过快 而导致丢包
|
||||||
|
conn.SetReadBuffer(64 * 1024)
|
||||||
|
conn.SetWriteBuffer(64 * 1024)
|
||||||
|
|
||||||
|
if isClient {
|
||||||
|
|
||||||
|
//客户端要自己循环读取udp
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
buf := utils.GetPacket()
|
||||||
|
n, _, err := conn.ReadFromUDP(buf)
|
||||||
|
|
||||||
|
inDataChan <- buf[:n] //该数据会被ReadMsg和 Read读到
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
return theUDPConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *UDPConn) ReadMsg() (b []byte, err error) {
|
||||||
|
|
||||||
|
select {
|
||||||
|
case msg, ok := <-uc.inMsgChan:
|
||||||
|
if !ok {
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
return msg, nil
|
||||||
|
|
||||||
|
case <-uc.readDeadline.Wait():
|
||||||
|
return nil, ErrTimeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (uc *UDPConn) GetReadChan() chan []byte {
|
||||||
|
return uc.inMsgChan
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *UDPConn) Read(buf []byte) (n int, err error) {
|
||||||
|
if len(uc.unread) > 0 {
|
||||||
|
n = copy(buf, uc.unread)
|
||||||
|
uc.unread = uc.unread[n:]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var msg []byte
|
||||||
|
msg, err = uc.ReadMsg()
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
n = copy(buf, msg)
|
||||||
|
|
||||||
|
diff := len(msg) - n
|
||||||
|
if diff > 0 { //最好不要分段读,否则我们将不会把缓存放回pool,总之建议buf直接使用 utils.GetPacket
|
||||||
|
|
||||||
|
uc.unread = append(uc.unread, msg[n:]...)
|
||||||
|
} else {
|
||||||
|
utils.PutPacket(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (uc *UDPConn) Write(buf []byte) (n int, err error) {
|
||||||
|
select {
|
||||||
|
case <-uc.writeDeadline.Wait():
|
||||||
|
return 0, ErrTimeout
|
||||||
|
default:
|
||||||
|
if uc.isClient {
|
||||||
|
time.Sleep(time.Millisecond) //不能发送太快,否则会出现丢包,实测简单1毫秒即可避免
|
||||||
|
|
||||||
|
/*
|
||||||
|
一些常见的丢包后出现的错误:
|
||||||
|
|
||||||
|
tls
|
||||||
|
bad mac
|
||||||
|
|
||||||
|
ws
|
||||||
|
non-zero rsv bits with no extension negotiated, Data: 0
|
||||||
|
|
||||||
|
裸奔:curl客户端:
|
||||||
|
curl: (56) LibreSSL SSL_read: error:1404C3FC:SSL routines:ST_OK:sslv3 alert bad record mac, errno 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
//if use writeToUDP at client end, we will get err Write write udp 127.0.0.1:50361->:60006: use of WriteTo with pre-connected connection
|
||||||
|
|
||||||
|
return uc.realConn.Write(buf)
|
||||||
|
} else {
|
||||||
|
return uc.realConn.WriteToUDP(buf, uc.peerAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*UDPConn) Close() error { return nil }
|
||||||
|
|
||||||
|
func (b *UDPConn) LocalAddr() net.Addr { return b.realConn.LocalAddr() }
|
||||||
|
func (b *UDPConn) RemoteAddr() net.Addr { return b.peerAddr }
|
||||||
|
func (b *UDPConn) RemoteUDPAddr() *net.UDPAddr { return b.peerAddr }
|
||||||
|
|
||||||
|
func (b *UDPConn) SetWriteDeadline(t time.Time) error {
|
||||||
|
b.writeDeadline.Set(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (b *UDPConn) SetReadDeadline(t time.Time) error {
|
||||||
|
b.readDeadline.Set(t)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (b *UDPConn) SetDeadline(t time.Time) error {
|
||||||
|
b.readDeadline.Set(t)
|
||||||
|
b.writeDeadline.Set(t)
|
||||||
|
return nil
|
||||||
|
}
|
@@ -10,6 +10,7 @@ const (
|
|||||||
MaxUDP_packetLen = 64 * 1024 // 关于 udp包数据长度,可参考 https://cloud.tencent.com/developer/article/1021196
|
MaxUDP_packetLen = 64 * 1024 // 关于 udp包数据长度,可参考 https://cloud.tencent.com/developer/article/1021196
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//本文件内含 一些 转发 udp 数据的 接口与方法
|
||||||
//////////////////// 接口 ////////////////////
|
//////////////////// 接口 ////////////////////
|
||||||
|
|
||||||
type UDPRequestReader interface {
|
type UDPRequestReader interface {
|
144
netLayer/udplistener.go
Normal file
144
netLayer/udplistener.go
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
package netLayer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hahahrfool/v2ray_simple/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
//UDPListener provide a udp listener which acts just like a tcp listener.
|
||||||
|
//It will block when called Accept.The Accept() returns a new Conn which
|
||||||
|
//just work for the communication between localhost and the remote dialer.
|
||||||
|
//
|
||||||
|
//UDPListener can also dial a remote host by calling NewConn.
|
||||||
|
// UDPListener 监听 UDPAddr,并不断对新远程地址 创建 新UDPConn,
|
||||||
|
// 并把读到的信息发送给 UDPConn;
|
||||||
|
// UDPListener 实现了 net.Listener
|
||||||
|
type UDPListener struct {
|
||||||
|
conn *net.UDPConn
|
||||||
|
|
||||||
|
newConnChan chan *UDPConn
|
||||||
|
connMap map[[6]byte]*UDPConn
|
||||||
|
mux sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPListener(laddr *net.UDPAddr) (*UDPListener, error) {
|
||||||
|
c, err := net.ListenUDP("udp4", laddr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewUDPListenerConn(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUDPListenerConn(conn *net.UDPConn) (*UDPListener, error) {
|
||||||
|
ul := new(UDPListener)
|
||||||
|
ul.conn = conn
|
||||||
|
ul.connMap = make(map[[6]byte]*UDPConn)
|
||||||
|
ul.newConnChan = make(chan *UDPConn, 100)
|
||||||
|
go ul.run()
|
||||||
|
|
||||||
|
return ul, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//It can be used to dial a remote udp
|
||||||
|
func (ul *UDPListener) NewConn(raddr *net.UDPAddr) *UDPConn {
|
||||||
|
addrb := UDPAddr2Byte(raddr)
|
||||||
|
return ul.newConn(raddr, addrb)
|
||||||
|
}
|
||||||
|
|
||||||
|
//newConn 创建一个新的 UDPConn,并存储在 ul.connMap 中
|
||||||
|
func (ul *UDPListener) newConn(raddr *net.UDPAddr, addrb [6]byte) *UDPConn {
|
||||||
|
newC := NewUDPConn(raddr, ul.conn, false)
|
||||||
|
ul.mux.Lock()
|
||||||
|
ul.connMap[addrb] = newC
|
||||||
|
ul.mux.Unlock()
|
||||||
|
return newC
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ul *UDPListener) DeleteConn(addrb [6]byte) {
|
||||||
|
ul.mux.Lock()
|
||||||
|
delete(ul.connMap, addrb)
|
||||||
|
ul.mux.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ul *UDPListener) Accept() (net.Conn, error) {
|
||||||
|
c, ok := <-ul.newConnChan
|
||||||
|
if !ok {
|
||||||
|
return nil, io.EOF
|
||||||
|
}
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//Once closed, it cannot be used again.
|
||||||
|
// it calls ul.CloseClients()
|
||||||
|
func (ul *UDPListener) Close() error {
|
||||||
|
|
||||||
|
err := ul.conn.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.CloseClients()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//UDPListener has a very fast way to close all the clients' connection.
|
||||||
|
// If the server side of the client connection is reading, it will get an EOF error.
|
||||||
|
// The application can then tell the remote client that it will be closed by sending a message using its custom protocol.
|
||||||
|
//
|
||||||
|
//Once closed, it cannot be used again.
|
||||||
|
func (ul *UDPListener) CloseClients() error {
|
||||||
|
close(ul.newConnChan)
|
||||||
|
|
||||||
|
ul.mux.Lock()
|
||||||
|
for _, c := range ul.connMap {
|
||||||
|
close(c.inMsgChan)
|
||||||
|
}
|
||||||
|
ul.mux.Unlock()
|
||||||
|
|
||||||
|
//err := ul.conn.Close()
|
||||||
|
//if err != nil {
|
||||||
|
// return err
|
||||||
|
//}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ul *UDPListener) Addr() net.Addr {
|
||||||
|
return ul.conn.LocalAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
//循环读取udp数据,对新连接会创建 UDPConn,然后把数据通过chan 传递给UDPConn
|
||||||
|
func (ul *UDPListener) run() {
|
||||||
|
conn := ul.conn
|
||||||
|
for {
|
||||||
|
buf := utils.GetPacket()
|
||||||
|
n, raddr, err := conn.ReadFromUDP(buf)
|
||||||
|
|
||||||
|
go func(theraddr *net.UDPAddr, thebuf []byte) {
|
||||||
|
addrb := UDPAddr2Byte(theraddr)
|
||||||
|
var oldConn *UDPConn
|
||||||
|
|
||||||
|
ul.mux.RLock()
|
||||||
|
oldConn = ul.connMap[addrb] //每次都从connMap查找是很慢的,先这样,以后使用更快的方法
|
||||||
|
ul.mux.RUnlock()
|
||||||
|
|
||||||
|
if oldConn == nil {
|
||||||
|
oldConn = ul.newConn(raddr, addrb)
|
||||||
|
|
||||||
|
ul.newConnChan <- oldConn //此时 ul 的 Accept的调用者就会收到一个新Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
oldConn.inMsgChan <- thebuf[:n]
|
||||||
|
|
||||||
|
}(raddr, buf)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -20,14 +20,14 @@ type CommonConf struct {
|
|||||||
Tag string `toml:"tag"` //可选
|
Tag string `toml:"tag"` //可选
|
||||||
Protocol string `toml:"protocol"` //约定,如果一个Protocol尾缀去掉了's'后仍然是一个有效协议,则该协议使用了 tls。这种方法继承自 v2simple,适合极简模式
|
Protocol string `toml:"protocol"` //约定,如果一个Protocol尾缀去掉了's'后仍然是一个有效协议,则该协议使用了 tls。这种方法继承自 v2simple,适合极简模式
|
||||||
Uuid string `toml:"uuid"` //一个用户的唯一标识,建议使用uuid,但也不一定
|
Uuid string `toml:"uuid"` //一个用户的唯一标识,建议使用uuid,但也不一定
|
||||||
Host string `toml:"host"` //ip 或域名.
|
Host string `toml:"host"` //ip 或域名. 若unix domain socket 则为文件路径
|
||||||
IP string `toml:"ip"` //给出Host后,该项可以省略; 既有Host又有ip的情况比较适合cdn
|
IP string `toml:"ip"` //给出Host后,该项可以省略; 既有Host又有ip的情况比较适合cdn
|
||||||
Port int `toml:"port"`
|
Port int `toml:"port"` //若Network不为 unix , 则port项必填
|
||||||
Version int `toml:"ver"` //可选
|
Version int `toml:"ver"` //可选
|
||||||
TLS bool `toml:"tls"` //可选. 如果不使用 's' 后缀法,则还可以配置这一项来更清晰第标明使用tls
|
TLS bool `toml:"tls"` //可选. 如果不使用 's' 后缀法,则还可以配置这一项来更清晰第标明使用tls
|
||||||
Insecure bool `toml:"insecure"` //tls 是否安全
|
Insecure bool `toml:"insecure"` //tls 是否安全
|
||||||
|
|
||||||
IsUDP bool `toml:"udp"` //默认使用tcp监听,如果udp项给出了,则用udp监听。一些即能监听udp又能监听tcp的协议就需要这一项配置.
|
Network string `toml:"network"` //默认使用tcp, network可选值为 tcp,udp, uds;
|
||||||
|
|
||||||
AdvancedLayer string `toml:"advancedLayer"` //可不填,或者为ws,或者为grpc
|
AdvancedLayer string `toml:"advancedLayer"` //可不填,或者为ws,或者为grpc
|
||||||
|
|
||||||
@@ -37,7 +37,15 @@ type CommonConf struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cc *CommonConf) GetAddr() string {
|
func (cc *CommonConf) GetAddr() string {
|
||||||
|
switch cc.Network {
|
||||||
|
case "unix":
|
||||||
|
return cc.Host
|
||||||
|
|
||||||
|
default:
|
||||||
return cc.Host + ":" + strconv.Itoa(cc.Port)
|
return cc.Host + ":" + strconv.Itoa(cc.Port)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监听所使用的设置, 使用者可以叫 listener 或者 inServer
|
// 监听所使用的设置, 使用者可以叫 listener 或者 inServer
|
||||||
|
@@ -231,8 +231,9 @@ func configCommon(ser ProxyCommon, cc *CommonConf) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//setIsDial(true),setDialConf(dc), call configCommon
|
//setNetwork, setIsDial(true),setDialConf(dc), call configCommon
|
||||||
func configCommonForClient(cli ProxyCommon, dc *DialConf) {
|
func configCommonForClient(cli ProxyCommon, dc *DialConf) {
|
||||||
|
cli.setNetwork(dc.Network)
|
||||||
cli.setIsDial(true)
|
cli.setIsDial(true)
|
||||||
cli.setDialConf(dc)
|
cli.setDialConf(dc)
|
||||||
cli.setTag(dc.Tag)
|
cli.setTag(dc.Tag)
|
||||||
@@ -244,8 +245,9 @@ func configCommonForClient(cli ProxyCommon, dc *DialConf) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//setTag, setCantRoute,setListenConf(lc), call configCommon
|
//setNetwork, setTag, setCantRoute,setListenConf(lc), call configCommon
|
||||||
func configCommonForServer(ser ProxyCommon, lc *ListenConf) {
|
func configCommonForServer(ser ProxyCommon, lc *ListenConf) {
|
||||||
|
ser.setNetwork(lc.Network)
|
||||||
ser.setListenConf(lc)
|
ser.setListenConf(lc)
|
||||||
ser.setTag(lc.Tag)
|
ser.setTag(lc.Tag)
|
||||||
ser.setCantRoute(lc.NoRoute)
|
ser.setCantRoute(lc.NoRoute)
|
||||||
|
@@ -53,11 +53,8 @@ type Server interface {
|
|||||||
// 这里认为, tcp/udp/kcp/raw_socket 是FirstName,具体的协议名称是 LastName, 中间层是 MiddleName
|
// 这里认为, tcp/udp/kcp/raw_socket 是FirstName,具体的协议名称是 LastName, 中间层是 MiddleName
|
||||||
// An Example of a full name: tcp+tls+ws+vless
|
// An Example of a full name: tcp+tls+ws+vless
|
||||||
func GetFullName(pc ProxyCommon) string {
|
func GetFullName(pc ProxyCommon) string {
|
||||||
if pc.IsUDP() {
|
|
||||||
return "udp" + pc.MiddleName() + pc.Name()
|
|
||||||
|
|
||||||
}
|
return pc.Network() + pc.MiddleName() + pc.Name()
|
||||||
return "tcp" + pc.MiddleName() + pc.Name()
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,40 +64,49 @@ type ProxyCommon interface {
|
|||||||
Name() string //代理协议名称, 如vless
|
Name() string //代理协议名称, 如vless
|
||||||
MiddleName() string //其它VSI层 所使用的协议,如 +tls+ws
|
MiddleName() string //其它VSI层 所使用的协议,如 +tls+ws
|
||||||
|
|
||||||
AddrStr() string //地址,在server就是监听地址,在client就是拨号地址
|
/////////////////// 网络层/传输层 ///////////////////
|
||||||
|
|
||||||
|
// 地址,若tcp/udp的话则为 ip:port/host:port的形式, 若是uds则是文件路径 ,
|
||||||
|
// 在server就是监听地址,在client就是拨号地址
|
||||||
|
AddrStr() string
|
||||||
SetAddrStr(string)
|
SetAddrStr(string)
|
||||||
|
Network() string
|
||||||
|
|
||||||
CantRoute() bool //for inServer
|
CantRoute() bool //for inServer
|
||||||
GetTag() string
|
GetTag() string
|
||||||
|
|
||||||
IsUDP() bool
|
|
||||||
|
|
||||||
IsDial() bool //true则为 Dial 端,false 则为 Listen 端
|
IsDial() bool //true则为 Dial 端,false 则为 Listen 端
|
||||||
GetListenConf() *ListenConf
|
GetListenConf() *ListenConf
|
||||||
GetDialConf() *DialConf
|
GetDialConf() *DialConf
|
||||||
|
|
||||||
|
/////////////////// TLS层 ///////////////////
|
||||||
|
|
||||||
SetUseTLS()
|
SetUseTLS()
|
||||||
IsUseTLS() bool
|
IsUseTLS() bool
|
||||||
|
|
||||||
AdvancedLayer() string //如果使用了ws或者grpc,这个要返回 ws 或 grpc
|
|
||||||
|
|
||||||
GetTLS_Server() *tlsLayer.Server
|
GetTLS_Server() *tlsLayer.Server
|
||||||
GetTLS_Client() *tlsLayer.Client
|
GetTLS_Client() *tlsLayer.Client
|
||||||
|
|
||||||
setTLS_Server(*tlsLayer.Server)
|
setTLS_Server(*tlsLayer.Server)
|
||||||
setTLS_Client(*tlsLayer.Client)
|
setTLS_Client(*tlsLayer.Client)
|
||||||
|
|
||||||
setCantRoute(bool)
|
/////////////////// 高级层 ///////////////////
|
||||||
setTag(string)
|
|
||||||
setAdvancedLayer(string)
|
|
||||||
|
|
||||||
setIsDial(bool)
|
AdvancedLayer() string //如果使用了ws或者grpc,这个要返回 ws 或 grpc
|
||||||
setListenConf(*ListenConf) //for inServer
|
|
||||||
setDialConf(*DialConf) //for outClient
|
|
||||||
|
|
||||||
GetWS_Client() *ws.Client //for outClient
|
GetWS_Client() *ws.Client //for outClient
|
||||||
|
|
||||||
initWS_client() //for outClient
|
initWS_client() //for outClient
|
||||||
|
|
||||||
|
setCantRoute(bool)
|
||||||
|
setTag(string)
|
||||||
|
setAdvancedLayer(string)
|
||||||
|
setNetwork(string)
|
||||||
|
|
||||||
|
setIsDial(bool)
|
||||||
|
setListenConf(*ListenConf) //for inServer
|
||||||
|
setDialConf(*DialConf) //for outClient
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//use dc.Host, dc.Insecure, dc.Utls
|
//use dc.Host, dc.Insecure, dc.Utls
|
||||||
@@ -157,6 +163,7 @@ type ProxyCommonStruct struct {
|
|||||||
Addr string
|
Addr string
|
||||||
TLS bool
|
TLS bool
|
||||||
Tag string //可用于路由, 见 netLayer.route.go
|
Tag string //可用于路由, 见 netLayer.route.go
|
||||||
|
network string
|
||||||
|
|
||||||
tls_s *tlsLayer.Server
|
tls_s *tlsLayer.Server
|
||||||
tls_c *tlsLayer.Client
|
tls_c *tlsLayer.Client
|
||||||
@@ -172,8 +179,8 @@ type ProxyCommonStruct struct {
|
|||||||
ws_c *ws.Client
|
ws_c *ws.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pcs *ProxyCommonStruct) IsUDP() bool {
|
func (pcs *ProxyCommonStruct) Network() string {
|
||||||
return false
|
return pcs.network
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pcs *ProxyCommonStruct) MiddleName() string {
|
func (pcs *ProxyCommonStruct) MiddleName() string {
|
||||||
@@ -198,6 +205,15 @@ func (pcs *ProxyCommonStruct) GetTag() string {
|
|||||||
func (pcs *ProxyCommonStruct) setTag(tag string) {
|
func (pcs *ProxyCommonStruct) setTag(tag string) {
|
||||||
pcs.Tag = tag
|
pcs.Tag = tag
|
||||||
}
|
}
|
||||||
|
func (pcs *ProxyCommonStruct) setNetwork(net string) {
|
||||||
|
if net == "" {
|
||||||
|
pcs.network = "tcp"
|
||||||
|
|
||||||
|
} else {
|
||||||
|
pcs.network = net
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (pcs *ProxyCommonStruct) setCantRoute(cr bool) {
|
func (pcs *ProxyCommonStruct) setCantRoute(cr bool) {
|
||||||
pcs.cantRoute = cr
|
pcs.cantRoute = cr
|
||||||
|
@@ -154,7 +154,8 @@ func (s *Server) Handshake(underlay net.Conn) (io.ReadWriter, *netLayer.Addr, er
|
|||||||
IP: theIP,
|
IP: theIP,
|
||||||
Name: theName,
|
Name: theName,
|
||||||
Port: thePort,
|
Port: thePort,
|
||||||
IsUDP: true,
|
//IsUDP: true,
|
||||||
|
Network: "udp",
|
||||||
}
|
}
|
||||||
|
|
||||||
serverAtyp, serverAddr, _, err := netLayer.ParseStrToAddr(s.Addr)
|
serverAtyp, serverAddr, _, err := netLayer.ParseStrToAddr(s.Addr)
|
||||||
@@ -339,7 +340,7 @@ func (u *UDPConn) StartReadRequest(udpPutter netLayer.UDP_Putter) {
|
|||||||
IP: theIP,
|
IP: theIP,
|
||||||
Name: theName,
|
Name: theName,
|
||||||
Port: thePort,
|
Port: thePort,
|
||||||
IsUDP: true,
|
Network: "udp",
|
||||||
}
|
}
|
||||||
|
|
||||||
udpPutter.WriteUDPRequest(thisaddr.ToUDPAddr(), bs[newStart:n-newStart])
|
udpPutter.WriteUDPRequest(thisaddr.ToUDPAddr(), bs[newStart:n-newStart])
|
||||||
|
@@ -41,7 +41,7 @@ type Client struct {
|
|||||||
type ClientCreator struct{}
|
type ClientCreator struct{}
|
||||||
|
|
||||||
func (_ ClientCreator) NewClientFromURL(u *url.URL) (proxy.Client, error) {
|
func (_ ClientCreator) NewClientFromURL(u *url.URL) (proxy.Client, error) {
|
||||||
return NewClient(u)
|
return NewClientByURL(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_ ClientCreator) NewClient(dc *proxy.DialConf) (proxy.Client, error) {
|
func (_ ClientCreator) NewClient(dc *proxy.DialConf) (proxy.Client, error) {
|
||||||
@@ -70,7 +70,7 @@ func (_ ClientCreator) NewClient(dc *proxy.DialConf) (proxy.Client, error) {
|
|||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(url *url.URL) (proxy.Client, error) {
|
func NewClientByURL(url *url.URL) (proxy.Client, error) {
|
||||||
addr := url.Host
|
addr := url.Host
|
||||||
uuidStr := url.User.Username()
|
uuidStr := url.User.Username()
|
||||||
id, err := proxy.NewV2rayUser(uuidStr)
|
id, err := proxy.NewV2rayUser(uuidStr)
|
||||||
@@ -119,7 +119,7 @@ func (c *Client) Handshake(underlay net.Conn, target *netLayer.Addr) (io.ReadWri
|
|||||||
addr, atyp := target.AddressBytes()
|
addr, atyp := target.AddressBytes()
|
||||||
|
|
||||||
cmd := CmdTCP
|
cmd := CmdTCP
|
||||||
if target.IsUDP {
|
if target.Network == "udp" {
|
||||||
if c.version == 1 && !c.is_CRUMFURS_established {
|
if c.version == 1 && !c.is_CRUMFURS_established {
|
||||||
|
|
||||||
//log.Println("尝试拨号 Cmd_CRUMFURS 信道")
|
//log.Println("尝试拨号 Cmd_CRUMFURS 信道")
|
||||||
@@ -175,7 +175,7 @@ func (c *Client) Handshake(underlay net.Conn, target *netLayer.Addr) (io.ReadWri
|
|||||||
Conn: underlay,
|
Conn: underlay,
|
||||||
uuid: *c.user,
|
uuid: *c.user,
|
||||||
version: c.version,
|
version: c.version,
|
||||||
isUDP: target.IsUDP,
|
isUDP: target.Network == "udp",
|
||||||
}, err
|
}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -320,7 +320,7 @@ realPart:
|
|||||||
addr.Port = int(binary.BigEndian.Uint16(portbs))
|
addr.Port = int(binary.BigEndian.Uint16(portbs))
|
||||||
|
|
||||||
if commandByte == CmdUDP {
|
if commandByte == CmdUDP {
|
||||||
addr.IsUDP = true
|
addr.Network = "udp"
|
||||||
}
|
}
|
||||||
|
|
||||||
var ip_or_domain_bytesLength byte = 0
|
var ip_or_domain_bytesLength byte = 0
|
||||||
@@ -388,7 +388,7 @@ realPart:
|
|||||||
optionalReader: io.MultiReader(readbuf, underlay),
|
optionalReader: io.MultiReader(readbuf, underlay),
|
||||||
uuid: thisUUIDBytes,
|
uuid: thisUUIDBytes,
|
||||||
version: int(version),
|
version: int(version),
|
||||||
isUDP: addr.IsUDP,
|
isUDP: addr.Network == "udp",
|
||||||
isServerEnd: true,
|
isServerEnd: true,
|
||||||
}, addr, nil
|
}, addr, nil
|
||||||
|
|
||||||
|
@@ -204,7 +204,7 @@ func testVLessUDP(version int, port string, t *testing.T) {
|
|||||||
targetStruct_forFakeUDPServer := &netLayer.Addr{
|
targetStruct_forFakeUDPServer := &netLayer.Addr{
|
||||||
Name: "127.0.0.1",
|
Name: "127.0.0.1",
|
||||||
Port: thePort,
|
Port: thePort,
|
||||||
IsUDP: true,
|
Network: "udp",
|
||||||
}
|
}
|
||||||
// 监听 Client End LocalServer
|
// 监听 Client End LocalServer
|
||||||
listener, err := net.Listen("tcp", fakeServerEndLocalServer.AddrStr())
|
listener, err := net.Listen("tcp", fakeServerEndLocalServer.AddrStr())
|
||||||
@@ -232,7 +232,7 @@ func testVLessUDP(version int, port string, t *testing.T) {
|
|||||||
|
|
||||||
remoteAddrStr := targetAddr.String()
|
remoteAddrStr := targetAddr.String()
|
||||||
|
|
||||||
if remoteAddrStr != targetStr_forFakeUDPServer || targetAddr.IsUDP == false {
|
if remoteAddrStr != targetStr_forFakeUDPServer || targetAddr.Network != "udp" {
|
||||||
t.Log("remoteAddrStr != targetStr_forFakeUDPServer || targetAddr.IsUDP == false ")
|
t.Log("remoteAddrStr != targetStr_forFakeUDPServer || targetAddr.IsUDP == false ")
|
||||||
t.Fail()
|
t.Fail()
|
||||||
return
|
return
|
||||||
|
38
version.go
38
version.go
@@ -1,3 +1,39 @@
|
|||||||
|
/*Package main 读取配置文件,将其内容转化为 proxy.Client和 proxy.Server,然后进行代理转发.
|
||||||
|
|
||||||
|
命令行参数请使用 --help查看详情。
|
||||||
|
|
||||||
|
如果一个命令行参数无法在标准配置中进行配置,那么它就属于高级选项,或者不推荐的选项,或者正在开发中的功能.
|
||||||
|
|
||||||
|
Config Format 配置格式
|
||||||
|
|
||||||
|
一共有三种配置格式,极简模式,标准模式,兼容模式。
|
||||||
|
|
||||||
|
“极简模式”(即 verysimple mode),入口和出口仅有一个,而且都是使用共享链接的url格式来配置.
|
||||||
|
|
||||||
|
标准模式使用toml格式。
|
||||||
|
|
||||||
|
兼容模式可以兼容v2ray现有json格式。(暂未实现)。
|
||||||
|
|
||||||
|
极简模式的理念是,配置文件的字符尽量少,尽量短小精悍;
|
||||||
|
|
||||||
|
还有个命令行模式,就是直接把极简模式的url 放到命令行参数中,比如:
|
||||||
|
|
||||||
|
verysimple -L socks5://sfdfsaf -D direct://
|
||||||
|
|
||||||
|
|
||||||
|
Structure 本项目结构
|
||||||
|
|
||||||
|
main -> proxy.Standard(配置文件) -> netLayer-> tlsLayer -> httpLayer -> ws -> proxy.
|
||||||
|
|
||||||
|
main中,读取配置文件,生成 Dail、Listen 和 RoutePolicy 对象后,开始监听;
|
||||||
|
|
||||||
|
用 netLayer操纵路由,用tlsLayer嗅探tls,用httpLayer操纵回落,可选经过ws, 然后都搞好后,传到proxy,然后就开始转发
|
||||||
|
|
||||||
|
Other - 其他
|
||||||
|
|
||||||
|
另外,本作暂时不考虑引入外界log包。依赖越少越好。
|
||||||
|
|
||||||
|
*/
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -10,6 +46,8 @@ import (
|
|||||||
var Version string //版本号由 Makefile 里的 BUILD_VERSION 指定
|
var Version string //版本号由 Makefile 里的 BUILD_VERSION 指定
|
||||||
|
|
||||||
func printVersion() {
|
func printVersion() {
|
||||||
|
const desc = "verysimple, a very simple implementation of V2Ray with some innovation"
|
||||||
|
|
||||||
fmt.Printf("===============================\nverysimple %v (%v), %v %v %v\n", Version, desc, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
fmt.Printf("===============================\nverysimple %v (%v), %v %v %v\n", Version, desc, runtime.Version(), runtime.GOOS, runtime.GOARCH)
|
||||||
fmt.Println("Support tls and websocket for all protocols.")
|
fmt.Println("Support tls and websocket for all protocols.")
|
||||||
if netLayer.HasEmbedGeoip() {
|
if netLayer.HasEmbedGeoip() {
|
||||||
|
@@ -21,6 +21,7 @@ tag = "my_vlesss1" # 可选, 但不可与其他tag重复
|
|||||||
protocol = "vlesss" # vless 的 尾缀s 表示 使用tls
|
protocol = "vlesss" # vless 的 尾缀s 表示 使用tls
|
||||||
uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" # 这个只是一个示例uuid,请自己生成一个新的.
|
uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" # 这个只是一个示例uuid,请自己生成一个新的.
|
||||||
host = "127.0.0.1"
|
host = "127.0.0.1"
|
||||||
|
#network = "udp" #network 目前支持 udp,tcp 和unix;如果不给出则默认为 tcp
|
||||||
|
|
||||||
# ip = "127.0.0.1"
|
# ip = "127.0.0.1"
|
||||||
|
|
||||||
|
@@ -14,3 +14,4 @@ insecure = true
|
|||||||
utls = true
|
utls = true
|
||||||
advancedLayer = "ws"
|
advancedLayer = "ws"
|
||||||
path = "/ohmygod_verysimple_is_very_simple" #为了防探测这里越长越随机越好
|
path = "/ohmygod_verysimple_is_very_simple" #为了防探测这里越长越随机越好
|
||||||
|
network = "udp"
|
@@ -10,6 +10,7 @@ cert = "cert.pem"
|
|||||||
key = "cert.key"
|
key = "cert.key"
|
||||||
advancedLayer = "ws"
|
advancedLayer = "ws"
|
||||||
path = "/ohmygod_verysimple_is_very_simple"
|
path = "/ohmygod_verysimple_is_very_simple"
|
||||||
|
network = "udp"
|
||||||
|
|
||||||
[[dial]]
|
[[dial]]
|
||||||
protocol = "direct"
|
protocol = "direct"
|
||||||
|
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/hahahrfool/v2ray_simple/utils"
|
"github.com/hahahrfool/v2ray_simple/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 因为 gobwas/ws 不包装conn,在写入和读取二进制时需要使用 wsutil包的特殊函数才行,
|
// 因为 gobwas/ws 不包装conn,在写入和读取二进制时需要使用 较为底层的函数才行,并未被提供标准的Read和Write
|
||||||
// 因此我们包装一下,统一使用Read和Write函数 来读写 二进制数据。因为我们这里是代理,
|
// 因此我们包装一下,统一使用Read和Write函数 来读写 二进制数据。因为我们这里是代理,
|
||||||
// 所以我们默认 抛弃 websocket的 数据帧 长度。
|
// 所以我们默认 抛弃 websocket的 数据帧 长度。
|
||||||
// 如果以后考虑与 vless v1的 udp 相结合的话,则数据包长度 不能丢弃,需要使用另外的实现。
|
// 如果以后考虑与 vless v1的 udp 相结合的话,则数据包长度 不能丢弃,需要使用另外的实现。
|
||||||
@@ -44,6 +44,7 @@ func (c *Conn) Read(p []byte) (int, error) {
|
|||||||
return n, e
|
return n, e
|
||||||
}
|
}
|
||||||
c.remainLenForLastFrame -= int64(n)
|
c.remainLenForLastFrame -= int64(n)
|
||||||
|
// 这里之所以可以放心 减去 n,不怕减成负的,是因为 r的代码里 在读取一帧的数据时,用到了 io.LimitedReader, 一帧的读取长度的上限已被限定,直到 该帧完全被读完为止
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -22,10 +22,9 @@ func Handshake(path string, underlay net.Conn) (net.Conn, error) {
|
|||||||
// 我们的 httpLayer 的 过滤方法仍然是最安全的,可以杜绝 所有非法数据;
|
// 我们的 httpLayer 的 过滤方法仍然是最安全的,可以杜绝 所有非法数据;
|
||||||
// 而 ws.Upgrader.Upgrade 使用了 readLine 函数。如果客户提供一个非法的超长的一行的话,它就会陷入泥淖
|
// 而 ws.Upgrader.Upgrade 使用了 readLine 函数。如果客户提供一个非法的超长的一行的话,它就会陷入泥淖
|
||||||
// 这个以后 可以先用 httpLayer的过滤方法,过滤掉后,再用 MultiReader组装回来,提供给 upgrader.Upgrade
|
// 这个以后 可以先用 httpLayer的过滤方法,过滤掉后,再用 MultiReader组装回来,提供给 upgrader.Upgrade
|
||||||
//目前设一个 ReadBufferSize即可, 看了,默认是 4096,已经够大
|
// ReadBufferSize默认是 4096,已经够大
|
||||||
|
|
||||||
upgrader := &ws.Upgrader{
|
upgrader := &ws.Upgrader{
|
||||||
//ReadBufferSize: 1,
|
|
||||||
OnRequest: func(uri []byte) error {
|
OnRequest: func(uri []byte) error {
|
||||||
struri := string(uri)
|
struri := string(uri)
|
||||||
if struri != path {
|
if struri != path {
|
||||||
@@ -40,7 +39,6 @@ func Handshake(path string, underlay net.Conn) (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err := upgrader.Upgrade(underlay)
|
_, err := upgrader.Upgrade(underlay)
|
||||||
//log.Println(hs, err)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -54,7 +52,6 @@ func Handshake(path string, underlay net.Conn) (net.Conn, error) {
|
|||||||
//不想客户端;服务端是不怕客户端在握手阶段传来任何多余数据的
|
//不想客户端;服务端是不怕客户端在握手阶段传来任何多余数据的
|
||||||
// 因为我们还没实现 0-rtt
|
// 因为我们还没实现 0-rtt
|
||||||
theConn.r.OnIntermediate = wsutil.ControlFrameHandler(underlay, ws.StateServerSide)
|
theConn.r.OnIntermediate = wsutil.ControlFrameHandler(underlay, ws.StateServerSide)
|
||||||
//theConn.w.DisableFlush() //发现分片的话,会出问题,所以就先关了. 搞清楚分片的问题再说。
|
|
||||||
|
|
||||||
return theConn, nil
|
return theConn, nil
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user