添加自定义网络层功能;添加udp网络层支持

配置中,listen和dial中,可添加 network = "udp" 字段,不添加则默认tcp
This commit is contained in:
hahahrfool
2022-03-21 21:19:31 +08:00
parent 120c050972
commit 548675e002
23 changed files with 638 additions and 166 deletions

View File

@@ -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
import (

88
main.go
View File

@@ -9,9 +9,7 @@ import (
"os"
"os/signal"
"runtime"
"strings"
"syscall"
"time"
"github.com/hahahrfool/v2ray_simple/httpLayer"
"github.com/hahahrfool/v2ray_simple/netLayer"
@@ -35,8 +33,6 @@ const (
)
var (
desc = "v2ray_simple, a very simple implementation of V2Ray, 并且在某些地方试图走在v2ray前面"
configFileName string
uniqueTestDomain string //有时需要测试到单一网站的流量,此时为了避免其它干扰,需要在这里声明 一下 该域名,然后程序里会进行过滤
@@ -55,17 +51,17 @@ var (
serversTagMap = make(map[string]proxy.Server)
clientsTagMap = make(map[string]proxy.Client)
listenURL string
dialURL string
tls_lazy_encrypt bool
tls_lazy_secure bool
routePolicy *netLayer.RoutePolicy
listenURL string
dialURL string
isServerEnd bool
mainFallback *httpLayer.ClassicFallback
isServerEnd bool //这个是代码里推断的不一定准确不过目前仅被用于tls lazy encrypt所以不是很重要
)
func init() {
@@ -206,7 +202,9 @@ func main() {
if confMode == simpleMode {
go listenSer(nil, defaultInServer)
} 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) {
var err error
if listener == nil {
listener, err = net.Listen("tcp", inServer.AddrStr())
theFunc := func(conn net.Conn) {
handleNewIncomeConnection(inServer, conn)
}
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 {
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) {
@@ -325,6 +290,8 @@ func handleNewIncomeConnection(inServer proxy.Server, thisLocalConnectionInstanc
}
//log.Println("handshake passed tls")
if adv := inServer.AdvancedLayer(); adv != "" {
if adv == "ws" {
wsConn, err := ws.Handshake(inServer.GetListenConf().Path, thisLocalConnectionInstance)
@@ -463,7 +430,7 @@ afterLocalServerHandshake:
}
//如果目标是udp则要分情况讨论
if targetAddr.IsUDP {
if targetAddr.Network == "udp" {
switch inServer.Name() {
case "vlesss":
@@ -535,7 +502,7 @@ afterLocalServerHandshake:
// 因为direct使用 proxy.RelayUDP_to_Direct 函数 直接实现了fullcone
// 那么我们只需要传入一个 UDP_Extractor 即可
if targetAddr.IsUDP {
if targetAddr.Network == "udp" {
var unknownRemoteAddrMsgWriter netLayer.UDPResponseWriter
@@ -587,12 +554,13 @@ afterLocalServerHandshake:
//log.Println("will dial", client.AddrStr())
realTargetAddr, _ = netLayer.NewAddr(client.AddrStr())
realTargetAddr.Network = client.Network()
}
clientConn, err := realTargetAddr.Dial()
if err != nil {
if utils.CanLogErr() {
log.Println("failed in dial", targetAddr.String(), ", Reason: ", err)
log.Println("failed in dial", realTargetAddr.String(), ", Reason: ", err)
}
return

View File

@@ -4,6 +4,7 @@ import (
"net"
"net/url"
"strconv"
"unsafe"
)
// Atyp, for vless and vmess; 注意与 trojan和socks5的区别trojan和socks5的相同含义的值是134
@@ -15,17 +16,20 @@ const (
// Addr represents a address that you want to access by proxy. Either Name or IP is used exclusively.
type Addr struct {
Name string // domain name
Name string // domain name, 或者 unix domain socket 的 文件路径
IP net.IP
Port int
IsUDP bool
//IsUDP bool
Network string
}
func NewAddrFromUDPAddr(addr *net.UDPAddr) *Addr {
return &Addr{
IP: addr.IP,
Port: addr.Port,
IsUDP: true,
//IsUDP: true,
Network: "udp",
}
}
@@ -74,9 +78,12 @@ func NewAddrByURL(addrStr string) (*Addr, error) {
a.Name = host
}
a.Network = u.Scheme
/*
if u.Scheme == "udp" {
a.IsUDP = true
}
}*/
return a, nil
}
@@ -92,14 +99,12 @@ func (a *Addr) String() string {
func (a *Addr) UrlString() string {
str := a.String()
if a.IsUDP {
return "udp://" + str
}
return "tcp://" + str
return a.Network + "://" + str
}
func (a *Addr) ToUDPAddr() *net.UDPAddr {
if !a.IsUDP {
if a.Network != "udp" {
return nil
}
ua, err := net.ResolveUDPAddr("udp", a.String())
@@ -118,10 +123,19 @@ func (a *Addr) HostStr() string {
}
func (addr *Addr) Dial() (net.Conn, error) {
if addr.IsUDP {
return net.Dial("udp", addr.String())
}
//log.Println("Dial called", addr, addr.Network)
switch addr.Network {
case "":
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
@@ -193,3 +207,16 @@ func ParseStrToAddr(s string) (atyp byte, addr []byte, port_uint16 uint16, err e
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
}

View File

@@ -1,10 +1,11 @@
package netLayer
const (
// transport Layer
// transport Layer, 使用uint16 mask所以最多支持16种
TCP uint16 = 1 << iota
UDP
UNIX //unix domain socket
Raw_socket
KCP
Quic //quic是一个横跨多个层的协议这里也算一个毕竟与kcp类似
@@ -19,6 +20,8 @@ func StrToTransportProtocol(s string) uint16 {
return TCP
case "udp":
return UDP
case "unix":
return UNIX
case "raw":
return Raw_socket
case "kcp":

75
netLayer/deadline.go Normal file
View 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
View 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
}

View File

@@ -73,6 +73,11 @@ func (sg *RouteSet) IsTransportProtocolAllowed(p uint16) bool {
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 {
return sg.IsTransportProtocolAllowed(UDP)
}
@@ -85,7 +90,7 @@ func (sg *RouteSet) IsAddrIn(a *Addr) bool {
//我们先过滤传输层,再过滤网络层
//目前我们仅支持udp和tcp这两种传输层协议所以可以如此。以后如果加了更多的话还需改动
if a.IsUDP && !sg.IsUDPAllowed() || !a.IsUDP && !sg.IsTCPAllowed() {
if !sg.IsAddrNetworkAllowed(a) {
return false
} else if sg.NetRanger == nil && sg.IPs == nil && sg.Domains == nil && sg.InTags == nil && sg.Countries == nil {

157
netLayer/udpConn.go Normal file
View 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
}

View File

@@ -10,6 +10,7 @@ const (
MaxUDP_packetLen = 64 * 1024 // 关于 udp包数据长度可参考 https://cloud.tencent.com/developer/article/1021196
)
//本文件内含 一些 转发 udp 数据的 接口与方法
//////////////////// 接口 ////////////////////
type UDPRequestReader interface {

144
netLayer/udplistener.go Normal file
View 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
}
}
}

View File

@@ -20,14 +20,14 @@ type CommonConf struct {
Tag string `toml:"tag"` //可选
Protocol string `toml:"protocol"` //约定如果一个Protocol尾缀去掉了's'后仍然是一个有效协议,则该协议使用了 tls。这种方法继承自 v2simple适合极简模式
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
Port int `toml:"port"`
Port int `toml:"port"` //若Network不为 unix , 则port项必填
Version int `toml:"ver"` //可选
TLS bool `toml:"tls"` //可选. 如果不使用 's' 后缀法则还可以配置这一项来更清晰第标明使用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
@@ -37,7 +37,15 @@ type CommonConf struct {
}
func (cc *CommonConf) GetAddr() string {
switch cc.Network {
case "unix":
return cc.Host
default:
return cc.Host + ":" + strconv.Itoa(cc.Port)
}
}
// 监听所使用的设置, 使用者可以叫 listener 或者 inServer

View File

@@ -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) {
cli.setNetwork(dc.Network)
cli.setIsDial(true)
cli.setDialConf(dc)
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) {
ser.setNetwork(lc.Network)
ser.setListenConf(lc)
ser.setTag(lc.Tag)
ser.setCantRoute(lc.NoRoute)

View File

@@ -53,11 +53,8 @@ type Server interface {
// 这里认为, tcp/udp/kcp/raw_socket 是FirstName具体的协议名称是 LastName, 中间层是 MiddleName
// An Example of a full name: tcp+tls+ws+vless
func GetFullName(pc ProxyCommon) string {
if pc.IsUDP() {
return "udp" + pc.MiddleName() + pc.Name()
}
return "tcp" + pc.MiddleName() + pc.Name()
return pc.Network() + pc.MiddleName() + pc.Name()
}
@@ -67,40 +64,49 @@ type ProxyCommon interface {
Name() string //代理协议名称, 如vless
MiddleName() string //其它VSI层 所使用的协议,如 +tls+ws
AddrStr() string //地址在server就是监听地址在client就是拨号地址
/////////////////// 网络层/传输层 ///////////////////
// 地址,若tcp/udp的话则为 ip:port/host:port的形式, 若是uds则是文件路径
// 在server就是监听地址在client就是拨号地址
AddrStr() string
SetAddrStr(string)
Network() string
CantRoute() bool //for inServer
GetTag() string
IsUDP() bool
IsDial() bool //true则为 Dial 端false 则为 Listen 端
GetListenConf() *ListenConf
GetDialConf() *DialConf
/////////////////// TLS层 ///////////////////
SetUseTLS()
IsUseTLS() bool
AdvancedLayer() string //如果使用了ws或者grpc这个要返回 ws 或 grpc
GetTLS_Server() *tlsLayer.Server
GetTLS_Client() *tlsLayer.Client
setTLS_Server(*tlsLayer.Server)
setTLS_Client(*tlsLayer.Client)
setCantRoute(bool)
setTag(string)
setAdvancedLayer(string)
/////////////////// 高级层 ///////////////////
setIsDial(bool)
setListenConf(*ListenConf) //for inServer
setDialConf(*DialConf) //for outClient
AdvancedLayer() string //如果使用了ws或者grpc这个要返回 ws 或 grpc
GetWS_Client() *ws.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
@@ -157,6 +163,7 @@ type ProxyCommonStruct struct {
Addr string
TLS bool
Tag string //可用于路由, 见 netLayer.route.go
network string
tls_s *tlsLayer.Server
tls_c *tlsLayer.Client
@@ -172,8 +179,8 @@ type ProxyCommonStruct struct {
ws_c *ws.Client
}
func (pcs *ProxyCommonStruct) IsUDP() bool {
return false
func (pcs *ProxyCommonStruct) Network() string {
return pcs.network
}
func (pcs *ProxyCommonStruct) MiddleName() string {
@@ -198,6 +205,15 @@ func (pcs *ProxyCommonStruct) GetTag() string {
func (pcs *ProxyCommonStruct) setTag(tag string) {
pcs.Tag = tag
}
func (pcs *ProxyCommonStruct) setNetwork(net string) {
if net == "" {
pcs.network = "tcp"
} else {
pcs.network = net
}
}
func (pcs *ProxyCommonStruct) setCantRoute(cr bool) {
pcs.cantRoute = cr

View File

@@ -154,7 +154,8 @@ func (s *Server) Handshake(underlay net.Conn) (io.ReadWriter, *netLayer.Addr, er
IP: theIP,
Name: theName,
Port: thePort,
IsUDP: true,
//IsUDP: true,
Network: "udp",
}
serverAtyp, serverAddr, _, err := netLayer.ParseStrToAddr(s.Addr)
@@ -339,7 +340,7 @@ func (u *UDPConn) StartReadRequest(udpPutter netLayer.UDP_Putter) {
IP: theIP,
Name: theName,
Port: thePort,
IsUDP: true,
Network: "udp",
}
udpPutter.WriteUDPRequest(thisaddr.ToUDPAddr(), bs[newStart:n-newStart])

View File

@@ -41,7 +41,7 @@ type Client struct {
type ClientCreator struct{}
func (_ ClientCreator) NewClientFromURL(u *url.URL) (proxy.Client, error) {
return NewClient(u)
return NewClientByURL(u)
}
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
}
func NewClient(url *url.URL) (proxy.Client, error) {
func NewClientByURL(url *url.URL) (proxy.Client, error) {
addr := url.Host
uuidStr := url.User.Username()
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()
cmd := CmdTCP
if target.IsUDP {
if target.Network == "udp" {
if c.version == 1 && !c.is_CRUMFURS_established {
//log.Println("尝试拨号 Cmd_CRUMFURS 信道")
@@ -175,7 +175,7 @@ func (c *Client) Handshake(underlay net.Conn, target *netLayer.Addr) (io.ReadWri
Conn: underlay,
uuid: *c.user,
version: c.version,
isUDP: target.IsUDP,
isUDP: target.Network == "udp",
}, err
}

View File

@@ -320,7 +320,7 @@ realPart:
addr.Port = int(binary.BigEndian.Uint16(portbs))
if commandByte == CmdUDP {
addr.IsUDP = true
addr.Network = "udp"
}
var ip_or_domain_bytesLength byte = 0
@@ -388,7 +388,7 @@ realPart:
optionalReader: io.MultiReader(readbuf, underlay),
uuid: thisUUIDBytes,
version: int(version),
isUDP: addr.IsUDP,
isUDP: addr.Network == "udp",
isServerEnd: true,
}, addr, nil

View File

@@ -204,7 +204,7 @@ func testVLessUDP(version int, port string, t *testing.T) {
targetStruct_forFakeUDPServer := &netLayer.Addr{
Name: "127.0.0.1",
Port: thePort,
IsUDP: true,
Network: "udp",
}
// 监听 Client End LocalServer
listener, err := net.Listen("tcp", fakeServerEndLocalServer.AddrStr())
@@ -232,7 +232,7 @@ func testVLessUDP(version int, port string, t *testing.T) {
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.Fail()
return

View File

@@ -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
import (
@@ -10,6 +46,8 @@ import (
var Version string //版本号由 Makefile 里的 BUILD_VERSION 指定
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.Println("Support tls and websocket for all protocols.")
if netLayer.HasEmbedGeoip() {

View File

@@ -21,6 +21,7 @@ tag = "my_vlesss1" # 可选, 但不可与其他tag重复
protocol = "vlesss" # vless 的 尾缀s 表示 使用tls
uuid = "a684455c-b14f-11ea-bf0d-42010aaa0003" # 这个只是一个示例uuid,请自己生成一个新的.
host = "127.0.0.1"
#network = "udp" #network 目前支持 udp,tcp 和unix;如果不给出则默认为 tcp
# ip = "127.0.0.1"

View File

@@ -14,3 +14,4 @@ insecure = true
utls = true
advancedLayer = "ws"
path = "/ohmygod_verysimple_is_very_simple" #为了防探测这里越长越随机越好
network = "udp"

View File

@@ -10,6 +10,7 @@ cert = "cert.pem"
key = "cert.key"
advancedLayer = "ws"
path = "/ohmygod_verysimple_is_very_simple"
network = "udp"
[[dial]]
protocol = "direct"

View File

@@ -9,7 +9,7 @@ import (
"github.com/hahahrfool/v2ray_simple/utils"
)
// 因为 gobwas/ws 不包装conn在写入和读取二进制时需要使用 wsutil包的特殊函数才行
// 因为 gobwas/ws 不包装conn在写入和读取二进制时需要使用 较为底层的函数才行并未被提供标准的Read和Write
// 因此我们包装一下统一使用Read和Write函数 来读写 二进制数据。因为我们这里是代理,
// 所以我们默认 抛弃 websocket的 数据帧 长度。
// 如果以后考虑与 vless v1的 udp 相结合的话,则数据包长度 不能丢弃,需要使用另外的实现。
@@ -44,6 +44,7 @@ func (c *Conn) Read(p []byte) (int, error) {
return n, e
}
c.remainLenForLastFrame -= int64(n)
// 这里之所以可以放心 减去 n不怕减成负的是因为 r的代码里 在读取一帧的数据时,用到了 io.LimitedReader, 一帧的读取长度的上限已被限定,直到 该帧完全被读完为止
return n, nil
}

View File

@@ -22,10 +22,9 @@ func Handshake(path string, underlay net.Conn) (net.Conn, error) {
// 我们的 httpLayer 的 过滤方法仍然是最安全的,可以杜绝 所有非法数据;
// 而 ws.Upgrader.Upgrade 使用了 readLine 函数。如果客户提供一个非法的超长的一行的话,它就会陷入泥淖
// 这个以后 可以先用 httpLayer的过滤方法过滤掉后再用 MultiReader组装回来提供给 upgrader.Upgrade
//目前设一个 ReadBufferSize即可, 看了,默认是 4096已经够大
// ReadBufferSize默认是 4096已经够大
upgrader := &ws.Upgrader{
//ReadBufferSize: 1,
OnRequest: func(uri []byte) error {
struri := string(uri)
if struri != path {
@@ -40,7 +39,6 @@ func Handshake(path string, underlay net.Conn) (net.Conn, error) {
}
_, err := upgrader.Upgrade(underlay)
//log.Println(hs, err)
if err != nil {
return nil, err
}
@@ -54,7 +52,6 @@ func Handshake(path string, underlay net.Conn) (net.Conn, error) {
//不想客户端;服务端是不怕客户端在握手阶段传来任何多余数据的
// 因为我们还没实现 0-rtt
theConn.r.OnIntermediate = wsutil.ControlFrameHandler(underlay, ws.StateServerSide)
//theConn.w.DisableFlush() //发现分片的话,会出问题,所以就先关了. 搞清楚分片的问题再说。
return theConn, nil
}