diff --git a/README.md b/README.md index 6a396da..46c7b38 100644 --- a/README.md +++ b/README.md @@ -296,8 +296,6 @@ v0协议是直接兼容现有v2ray/xray的,比如可以客户端用任何现 支持trojan协议 以及smux, 而且经过测速,比trojan-go快。(速度差距和本作的vless与v2ray的vless的差距基本一致,所以就不放出测速文件了,参考vless即可) -不过 lazy特性是不支持trojan的。这种不稳定不安全的特性还是专用在一个协议上比较好。 - 在没有mmdb文件时,自动下载mmdb 使用readv 进行加速 @@ -506,11 +504,6 @@ MIT协议,即你用的时候也要附带一个MIT文件,然后作者不承 启发自我fork的v2simple,不过原作者的架构还是有点欠缺,我就直接完全重构了,完全使用我自己的代码。 -这样也杜绝了 原作者跑路 导致的 一些不懂法律的人对于开源许可的 质疑。 - -实际上是毫无问题的,关键是他们太谨慎。无所谓,现在我完全自己写,没话说了吧—; - -我fork也是尊重原作者,既然你们这么谨慎,正好推动了我的重构计划,推动了历史发展 ## 额外说明 以及 开发计划 计划有 diff --git a/docs/install.md b/docs/install.md index b3aa4d6..8766295 100644 --- a/docs/install.md +++ b/docs/install.md @@ -67,14 +67,17 @@ sudo cp examples/vlesss.server.toml server.toml 如果你不愿意使用linux的“后台服务”,只是想手动去令它在后台运行,那么实际上,在verysimple所在位置运行如下一段命令即可。 -``` -nohup ./verysimple -c server.toml >/dev/null & -``` + + nohup ./verysimple -c server.toml >/dev/null & + 这里将它的标准输出舍弃了,因为一般来说我们会在toml配置文件中 配置好日志文件名称;如果不舍弃输出的话,就会多一个输出(到控制台),增加系统负担。 同样,视你的权限来酌情在命令前面添加 `sudo` +上面这个nohub这行命令,可以写到一个文件里,比如 run.sh, 然后用 `chmod +x run.sh` 将其变为可执行文件, 之后你只要运行 run.sh 就可以后台运行了。 + +如果你会crontab,还可以在里面设置开机运行该脚本。这种方式就比 systemctl轻量多了,而且还适用于openwrt。 ### 标准方式 diff --git a/httpLayer/header.go b/httpLayer/header.go index 7710c37..d7d0144 100644 --- a/httpLayer/header.go +++ b/httpLayer/header.go @@ -172,9 +172,8 @@ func (h *HeaderPreset) AssignDefaultValue() { h.Prepare() } -func (h *HeaderPreset) ReadRequest(underlay net.Conn) (leftBuf *bytes.Buffer, err error) { +func (h *HeaderPreset) ReadRequest(underlay io.Reader) (rp H1RequestParser, leftBuf *bytes.Buffer, err error) { - var rp H1RequestParser err = rp.ReadAndParse(underlay) if err != nil { return @@ -193,14 +192,18 @@ func (h *HeaderPreset) ReadRequest(underlay net.Conn) (leftBuf *bytes.Buffer, er err = utils.ErrInErr{ErrDesc: "ReadRequest failed, wrong path", ErrDetail: utils.ErrInvalidData, Data: rp.Path} return } + allbytes := rp.WholeRequestBuf.Bytes() + + leftBuf = bytes.NewBuffer(allbytes) + indexOfEnding := bytes.Index(allbytes, HeaderENDING_bytes) if indexOfEnding < 0 { err = utils.ErrInvalidData return } - headerBytes := rp.WholeRequestBuf.Next(indexOfEnding) + headerBytes := leftBuf.Next(indexOfEnding) if h.Strict { indexOfFirstCRLF := bytes.Index(allbytes, []byte(CRLF)) @@ -258,12 +261,12 @@ func (h *HeaderPreset) ReadRequest(underlay net.Conn) (leftBuf *bytes.Buffer, er } - rp.WholeRequestBuf.Next(4) + leftBuf.Next(len(HeaderENDING)) - return rp.WholeRequestBuf, nil + return } -func (h *HeaderPreset) WriteRequest(underlay net.Conn, payload []byte) error { +func (h *HeaderPreset) WriteRequest(underlay io.Writer, payload []byte) error { buf := bytes.NewBuffer(payload) r, err := http.NewRequest(h.Request.Method, h.Request.Path[0], buf) @@ -281,7 +284,7 @@ func (h *HeaderPreset) WriteRequest(underlay net.Conn, payload []byte) error { return r.Write(underlay) } -func (h *HeaderPreset) ReadResponse(underlay net.Conn) (leftBuf *bytes.Buffer, err error) { +func (h *HeaderPreset) ReadResponse(underlay io.Reader) (leftBuf *bytes.Buffer, err error) { bs := utils.GetPacket() var n int @@ -307,7 +310,7 @@ func (h *HeaderPreset) ReadResponse(underlay net.Conn) (leftBuf *bytes.Buffer, e return buf, nil } -func (h *HeaderPreset) WriteResponse(underlay net.Conn, payload []byte) error { +func (h *HeaderPreset) WriteResponse(underlay io.Writer, payload []byte) error { buf := utils.GetBuf() buf.WriteString("HTTP/") @@ -347,6 +350,7 @@ type HeaderConn struct { IsServerEnd bool optionalReader io.Reader + ReadOkCallback func(*bytes.Buffer) notFirstWrite bool } @@ -356,11 +360,21 @@ func (c *HeaderConn) Read(p []byte) (n int, err error) { if c.IsServerEnd { if c.optionalReader == nil { - buf, err = c.H.ReadRequest(c.Conn) + var rp H1RequestParser + + rp, buf, err = c.H.ReadRequest(c.Conn) if err != nil { - err = utils.ErrInErr{ErrDesc: "http HeaderConn Read failed, at serverEnd", ErrDetail: err} + + err = &utils.ErrBuffer{ + Err: utils.ErrInErr{ErrDesc: "http HeaderConn Read failed, at serverEnd", ErrDetail: err}, + Buf: rp.WholeRequestBuf, + } return } + if c.ReadOkCallback != nil { + c.ReadOkCallback(rp.WholeRequestBuf) + + } c.optionalReader = io.MultiReader(buf, c.Conn) } diff --git a/httpLayer/httpLayer.go b/httpLayer/httpLayer.go index c7cac9f..5a01571 100644 --- a/httpLayer/httpLayer.go +++ b/httpLayer/httpLayer.go @@ -118,7 +118,7 @@ type H1RequestParser struct { Headers []RawHeader } -// 尝试读取数据并解析HTTP请求, 解析道道 数据会存入 RequestParser 结构中. +// 尝试读取数据并解析HTTP请求, 解析的 数据会存入 RequestParser 结构中. //如果读取错误,会返回该错误; 如果读到的不是HTTP请求,返回 的err 的 errors.Is(err,ErrNotHTTP_Request) == true; // // 该函数有个小问题,就是如果一次Read是短Read(可能是网络原因),没读完整个http头部,则仍然会认为是错误 diff --git a/iics.go b/iics.go index 3af6805..5dd60a2 100644 --- a/iics.go +++ b/iics.go @@ -91,6 +91,13 @@ type incomingInserverConnState struct { routedToDirect bool routingEnv *proxy.RoutingEnv //used in passToOutClient + + heapObj *heapObj +} + +type heapObj struct { + headerPass bool + wholeBuffer *bytes.Buffer } //每个iics使用之前,必须调用 genID @@ -103,7 +110,10 @@ func (iics *incomingInserverConnState) genID() { // 在调用 passToOutClient前遇到err时调用, 若找出了buf,设置iics,并返回true func (iics *incomingInserverConnState) extractFirstBufFromErr(err error) bool { - if !iics.inServer.CanFallback() { + var hasHeader = iics.inServer.HasHeader() != nil + var canFallback = iics.inServer.CanFallback() || hasHeader + + if !canFallback { if iics.wrappedConn != nil { iics.wrappedConn.Close() @@ -116,6 +126,15 @@ func (iics *incomingInserverConnState) extractFirstBufFromErr(err error) bool { be, ok := err.(*utils.ErrBuffer) if !ok { + if iics.heapObj != nil { + if iics.heapObj.headerPass && iics.heapObj.wholeBuffer != nil { + //http header通过了但是后面出错,有可能是类似 vmess+http 的情况,收到了一个正常的Get请求,后面的vmess读取不到数据导致了 read timeout,此时依然可以回落. + iics.fallbackFirstBuffer = iics.heapObj.wholeBuffer + iics.heapObj = nil + return true + + } + } // 能fallback 但是返回的 err却不是fallback err,证明遇到了更大问题,可能是底层read问题,所以也不用继续fallback了 if iics.wrappedConn != nil { iics.wrappedConn.Close() diff --git a/main.go b/main.go index 4b087af..3fae21f 100644 --- a/main.go +++ b/main.go @@ -249,10 +249,21 @@ func handleNewIncomeConnection(inServer proxy.Server, defaultClientForThis proxy //websocket 可以自行处理header, 不需要额外http包装 if !(advSer != nil && advSer.CanHandleHeaders()) { + var ho *heapObj = iics.heapObj + + if iics.heapObj == nil { + ho = &heapObj{} + iics.heapObj = ho + } + wrappedConn = &httpLayer.HeaderConn{ Conn: wrappedConn, H: header, IsServerEnd: true, + ReadOkCallback: func(b *bytes.Buffer) { + ho.headerPass = true + ho.wholeBuffer = b + }, } } diff --git a/proxy/vmess/client.go b/proxy/vmess/client.go index 0f6b037..83e74d6 100644 --- a/proxy/vmess/client.go +++ b/proxy/vmess/client.go @@ -13,7 +13,6 @@ import ( "math/rand" "net" "net/url" - "runtime" "strings" "time" @@ -23,8 +22,6 @@ import ( "golang.org/x/crypto/chacha20poly1305" ) -const systemAutoUseAes = runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x" || runtime.GOARCH == "arm64" - const vmess_security_confStr string = "vmess_security" func init() { @@ -110,7 +107,7 @@ func (c *Client) specifySecurityByStr(security string) error { case "chacha20-poly1305": c.security = SecurityChacha20Poly1305 case "auto", "": //这里我们为了保护用户,当字符串为空时,依然设为auto,而不是zero - if systemAutoUseAes { + if utils.SystemAutoUseAes { c.security = SecurityAES128GCM } else { c.security = SecurityChacha20Poly1305 diff --git a/utils/crypto.go b/utils/crypto.go new file mode 100644 index 0000000..b1e59dd --- /dev/null +++ b/utils/crypto.go @@ -0,0 +1,6 @@ +package utils + +import "runtime" + +//有些系统对aes支持不好,有些支持好。SystemAutoUseAes若为true,则说明支持很好,使用aes作为加密算法速度最佳。 +const SystemAutoUseAes = runtime.GOARCH == "amd64" || runtime.GOARCH == "s390x" || runtime.GOARCH == "arm64"