mirror of
https://github.com/nadoo/glider.git
synced 2025-02-23 17:35:40 +08:00
vmess: support chunk stream
This commit is contained in:
parent
e866062a8c
commit
b465dc1444
21
conf.go
21
conf.go
@ -2,9 +2,11 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/nadoo/conflag"
|
"github.com/nadoo/conflag"
|
||||||
)
|
)
|
||||||
@ -84,6 +86,25 @@ func confInit() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listDir(dirPth string, suffix string) (files []string, err error) {
|
||||||
|
files = make([]string, 0, 10)
|
||||||
|
dir, err := ioutil.ReadDir(dirPth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
PthSep := string(os.PathSeparator)
|
||||||
|
suffix = strings.ToUpper(suffix)
|
||||||
|
for _, fi := range dir {
|
||||||
|
if fi.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
|
||||||
|
files = append(files, dirPth+PthSep+fi.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
// RuleConf , every ruleForwarder points to a rule file
|
// RuleConf , every ruleForwarder points to a rule file
|
||||||
type RuleConf struct {
|
type RuleConf struct {
|
||||||
name string
|
name string
|
||||||
|
34
main.go
34
main.go
@ -25,7 +25,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// VERSION .
|
// VERSION .
|
||||||
const VERSION = "0.6.0"
|
const VERSION = "0.6.2"
|
||||||
|
|
||||||
func dialerFromConf() proxy.Dialer {
|
func dialerFromConf() proxy.Dialer {
|
||||||
// global forwarders in xx.conf
|
// global forwarders in xx.conf
|
||||||
@ -48,31 +48,16 @@ func dialerFromConf() proxy.Dialer {
|
|||||||
func main() {
|
func main() {
|
||||||
|
|
||||||
confInit()
|
confInit()
|
||||||
|
|
||||||
log.F = func(f string, v ...interface{}) {
|
log.F = func(f string, v ...interface{}) {
|
||||||
if conf.Verbose {
|
if conf.Verbose {
|
||||||
stdlog.Printf(f, v...)
|
stdlog.Printf(f, v...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sDialer := NewRuleDialer(conf.rules, dialerFromConf())
|
dialer := NewRuleDialer(conf.rules, dialerFromConf())
|
||||||
|
ipsetM, _ := NewIPSetManager(conf.IPSet, conf.rules)
|
||||||
for _, listen := range conf.Listen {
|
|
||||||
local, err := proxy.ServerFromURL(listen, sDialer)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
go local.ListenAndServe()
|
|
||||||
}
|
|
||||||
|
|
||||||
ipsetM, err := NewIPSetManager(conf.IPSet, conf.rules)
|
|
||||||
if err != nil {
|
|
||||||
log.F("create ipset manager error: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if conf.DNS != "" {
|
if conf.DNS != "" {
|
||||||
d, err := dns.NewDNS(conf.DNS, conf.DNSServer[0], sDialer, false)
|
d, err := dns.NewDNS(conf.DNS, conf.DNSServer[0], dialer, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -87,7 +72,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// add a handler to update proxy rules when a domain resolved
|
// add a handler to update proxy rules when a domain resolved
|
||||||
d.AddAnswerHandler(sDialer.AddDomainIP)
|
d.AddAnswerHandler(dialer.AddDomainIP)
|
||||||
if ipsetM != nil {
|
if ipsetM != nil {
|
||||||
d.AddAnswerHandler(ipsetM.AddDomainIP)
|
d.AddAnswerHandler(ipsetM.AddDomainIP)
|
||||||
}
|
}
|
||||||
@ -95,6 +80,15 @@ func main() {
|
|||||||
go d.ListenAndServe()
|
go d.ListenAndServe()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, listen := range conf.Listen {
|
||||||
|
local, err := proxy.ServerFromURL(listen, dialer)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
go local.ListenAndServe()
|
||||||
|
}
|
||||||
|
|
||||||
sigCh := make(chan os.Signal, 1)
|
sigCh := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
|
||||||
<-sigCh
|
<-sigCh
|
||||||
|
@ -1,3 +1,105 @@
|
|||||||
package vmess
|
package vmess
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
// chunk: plain, AES-128-CFB, AES-128-GCM, ChaCha20-Poly1305
|
// chunk: plain, AES-128-CFB, AES-128-GCM, ChaCha20-Poly1305
|
||||||
|
|
||||||
|
const maxChunkSize = 1 << 14 // 16384
|
||||||
|
const defaultChunkSize = 1 << 13 // 8192
|
||||||
|
|
||||||
|
type chunkedReader struct {
|
||||||
|
io.Reader
|
||||||
|
buf []byte
|
||||||
|
leftover []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChunkedReader(r io.Reader) io.Reader {
|
||||||
|
return &chunkedReader{
|
||||||
|
Reader: r,
|
||||||
|
buf: make([]byte, 2+maxChunkSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chunkedReader) read() (int, error) {
|
||||||
|
lenBuf := make([]byte, 2)
|
||||||
|
_, err := io.ReadFull(r.Reader, lenBuf)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
len := binary.BigEndian.Uint16(lenBuf)
|
||||||
|
|
||||||
|
buf := r.buf[:len]
|
||||||
|
_, err = io.ReadFull(r.Reader, buf)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(len), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *chunkedReader) Read(b []byte) (int, error) {
|
||||||
|
if len(r.leftover) > 0 {
|
||||||
|
n := copy(b, r.leftover)
|
||||||
|
r.leftover = r.leftover[n:]
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := r.read()
|
||||||
|
m := copy(b, r.buf[:n])
|
||||||
|
if m < n {
|
||||||
|
r.leftover = r.buf[m:n]
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type chunkedWriter struct {
|
||||||
|
io.Writer
|
||||||
|
buf []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func newChunkedWriter(w io.Writer) io.Writer {
|
||||||
|
return &chunkedWriter{
|
||||||
|
Writer: w,
|
||||||
|
buf: make([]byte, 2+maxChunkSize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *chunkedWriter) Write(b []byte) (int, error) {
|
||||||
|
n, err := w.ReadFrom(bytes.NewBuffer(b))
|
||||||
|
return int(n), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *chunkedWriter) ReadFrom(r io.Reader) (n int64, err error) {
|
||||||
|
for {
|
||||||
|
buf := w.buf
|
||||||
|
payloadBuf := buf[2 : 2+defaultChunkSize]
|
||||||
|
|
||||||
|
nr, er := r.Read(payloadBuf)
|
||||||
|
if nr > 0 {
|
||||||
|
n += int64(nr)
|
||||||
|
payloadBuf = payloadBuf[:nr]
|
||||||
|
binary.BigEndian.PutUint16(buf[:], uint16(nr))
|
||||||
|
|
||||||
|
_, ew := w.Writer.Write(buf)
|
||||||
|
if ew != nil {
|
||||||
|
err = ew
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if er != nil {
|
||||||
|
if er != io.EOF { // ignore EOF as per io.ReaderFrom contract
|
||||||
|
err = er
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
|
|
||||||
// Request Options
|
// Request Options
|
||||||
const (
|
const (
|
||||||
|
OptBasicFormat byte = 0
|
||||||
OptChunkStream byte = 1
|
OptChunkStream byte = 1
|
||||||
OptReuseTCPConnection byte = 2
|
OptReuseTCPConnection byte = 2
|
||||||
OptMetadataObfuscate byte = 4
|
OptMetadataObfuscate byte = 4
|
||||||
@ -43,12 +44,14 @@ const (
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
users []*User
|
users []*User
|
||||||
count int
|
count int
|
||||||
|
opt byte
|
||||||
security byte
|
security byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conn is a connection to vmess server
|
// Conn is a connection to vmess server
|
||||||
type Conn struct {
|
type Conn struct {
|
||||||
user *User
|
user *User
|
||||||
|
opt byte
|
||||||
security byte
|
security byte
|
||||||
|
|
||||||
atyp Atyp
|
atyp Atyp
|
||||||
@ -63,6 +66,9 @@ type Conn struct {
|
|||||||
|
|
||||||
net.Conn
|
net.Conn
|
||||||
connected bool
|
connected bool
|
||||||
|
|
||||||
|
dataReader io.Reader
|
||||||
|
dataWriter io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient .
|
// NewClient .
|
||||||
@ -78,6 +84,9 @@ func NewClient(uuidStr, security string, alterID int) (*Client, error) {
|
|||||||
c.users = append(c.users, user.GenAlterIDUsers(alterID)...)
|
c.users = append(c.users, user.GenAlterIDUsers(alterID)...)
|
||||||
c.count = len(c.users)
|
c.count = len(c.users)
|
||||||
|
|
||||||
|
// TODO: config?
|
||||||
|
c.opt = OptBasicFormat
|
||||||
|
|
||||||
security = strings.ToLower(security)
|
security = strings.ToLower(security)
|
||||||
switch security {
|
switch security {
|
||||||
case "aes-128-cfb":
|
case "aes-128-cfb":
|
||||||
@ -96,7 +105,7 @@ func NewClient(uuidStr, security string, alterID int) (*Client, error) {
|
|||||||
// NewConn .
|
// NewConn .
|
||||||
func (c *Client) NewConn(rc net.Conn, target string) (*Conn, error) {
|
func (c *Client) NewConn(rc net.Conn, target string) (*Conn, error) {
|
||||||
r := rand.Intn(c.count)
|
r := rand.Intn(c.count)
|
||||||
conn := &Conn{user: c.users[r], security: c.security}
|
conn := &Conn{user: c.users[r], opt: c.opt, security: c.security}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
conn.atyp, conn.addr, conn.port, err = ParseAddr(target)
|
conn.atyp, conn.addr, conn.port, err = ParseAddr(target)
|
||||||
@ -149,7 +158,7 @@ func (c *Conn) EncodeRequest() ([]byte, error) {
|
|||||||
buf.Write(c.reqBodyIV[:]) // IV
|
buf.Write(c.reqBodyIV[:]) // IV
|
||||||
buf.Write(c.reqBodyKey[:]) // Key
|
buf.Write(c.reqBodyKey[:]) // Key
|
||||||
buf.WriteByte(c.reqRespV) // V
|
buf.WriteByte(c.reqRespV) // V
|
||||||
buf.WriteByte(0) // Opt
|
buf.WriteByte(c.opt) // Opt
|
||||||
|
|
||||||
// pLen and Sec
|
// pLen and Sec
|
||||||
paddingLen := rand.Intn(16)
|
paddingLen := rand.Intn(16)
|
||||||
@ -218,9 +227,24 @@ func (c *Conn) Read(b []byte) (n int, err error) {
|
|||||||
c.DecodeRespHeader()
|
c.DecodeRespHeader()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.opt&OptChunkStream != 0 {
|
||||||
|
if c.dataReader == nil {
|
||||||
|
c.dataReader = newChunkedReader(c.Conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.dataReader.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
return c.Conn.Read(b)
|
return c.Conn.Read(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) Write(b []byte) (n int, err error) {
|
func (c *Conn) Write(b []byte) (n int, err error) {
|
||||||
|
if c.opt&OptChunkStream != 0 {
|
||||||
|
if c.dataWriter == nil {
|
||||||
|
c.dataWriter = newChunkedWriter(c.Conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.dataWriter.Write(b)
|
||||||
|
}
|
||||||
return c.Conn.Write(b)
|
return c.Conn.Write(b)
|
||||||
}
|
}
|
||||||
|
26
utils.go
26
utils.go
@ -1,26 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func listDir(dirPth string, suffix string) (files []string, err error) {
|
|
||||||
files = make([]string, 0, 10)
|
|
||||||
dir, err := ioutil.ReadDir(dirPth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
PthSep := string(os.PathSeparator)
|
|
||||||
suffix = strings.ToUpper(suffix)
|
|
||||||
for _, fi := range dir {
|
|
||||||
if fi.IsDir() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) {
|
|
||||||
files = append(files, dirPth+PthSep+fi.Name())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return files, nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user