Skip to content

Commit

Permalink
Feature: refactor vmess & add http network
Browse files Browse the repository at this point in the history
  • Loading branch information
Dreamacro committed Mar 31, 2020
1 parent 2067672 commit 19f809b
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 133 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,24 @@ proxies:
# ws-path: /path
# ws-headers:
# Host: v2ray.com

- name: "vmess-http"
type: vmess
server: server
port: 443
uuid: uuid
alterId: 32
cipher: auto
# udp: true
# network: http
# http-opts:
# # method: "GET"
# # path:
# # - '/'
# # - '/video'
# # headers:
# # Connection:
# # - keep-alive

# socks5
- name: "socks"
Expand Down
7 changes: 6 additions & 1 deletion adapters/outbound/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ func ParseProxy(mapping map[string]interface{}) (C.Proxy, error) {
}
proxy = NewHttp(*httpOption)
case "vmess":
vmessOption := &VmessOption{}
vmessOption := &VmessOption{
HTTPOpts: HTTPOptions{
Method: "GET",
Path: []string{"/"},
},
}
err = decoder.Decode(mapping, vmessOption)
if err != nil {
break
Expand Down
23 changes: 9 additions & 14 deletions adapters/outbound/shadowsocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package outbound

import (
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -156,21 +155,17 @@ func NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {
return nil, fmt.Errorf("ss %s obfs mode error: %s", addr, opts.Mode)
}
obfsMode = opts.Mode
v2rayOption = &v2rayObfs.Option{
Host: opts.Host,
Path: opts.Path,
Headers: opts.Headers,
Mux: opts.Mux,
}

var tlsConfig *tls.Config
if opts.TLS {
tlsConfig = &tls.Config{
ServerName: opts.Host,
InsecureSkipVerify: opts.SkipCertVerify,
ClientSessionCache: getClientSessionCache(),
}
}
v2rayOption = &v2rayObfs.Option{
Host: opts.Host,
Path: opts.Path,
Headers: opts.Headers,
TLSConfig: tlsConfig,
Mux: opts.Mux,
v2rayOption.TLS = true
v2rayOption.SkipCertVerify = opts.SkipCertVerify
v2rayOption.SessionCache = getClientSessionCache()
}
}

Expand Down
70 changes: 57 additions & 13 deletions adapters/outbound/vmess.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"net"
"net/http"
"strconv"
"strings"

Expand All @@ -17,6 +18,7 @@ import (
type Vmess struct {
*Base
client *vmess.Client
option *VmessOption
}

type VmessOption struct {
Expand All @@ -29,13 +31,60 @@ type VmessOption struct {
TLS bool `proxy:"tls,omitempty"`
UDP bool `proxy:"udp,omitempty"`
Network string `proxy:"network,omitempty"`
HTTPOpts HTTPOptions `proxy:"http-opts,omitempty"`
WSPath string `proxy:"ws-path,omitempty"`
WSHeaders map[string]string `proxy:"ws-headers,omitempty"`
SkipCertVerify bool `proxy:"skip-cert-verify,omitempty"`
}

type HTTPOptions struct {
Method string `proxy:"method,omitempty"`
Path []string `proxy:"path,omitempty"`
Headers map[string][]string `proxy:"headers,omitempty"`
}

func (v *Vmess) StreamConn(c net.Conn, metadata *C.Metadata) (net.Conn, error) {
return v.client.New(c, parseVmessAddr(metadata))
var err error
switch v.option.Network {
case "ws":
host, port, _ := net.SplitHostPort(v.addr)
wsOpts := &vmess.WebsocketConfig{
Host: host,
Port: port,
Path: v.option.WSPath,
}

if len(v.option.WSHeaders) != 0 {
header := http.Header{}
for key, value := range v.option.WSHeaders {
header.Add(key, value)
}
wsOpts.Headers = header
}

if v.option.TLS {
wsOpts.TLS = true
wsOpts.SessionCache = getClientSessionCache()
wsOpts.SkipCertVerify = v.option.SkipCertVerify
}
c, err = vmess.StreamWebsocketConn(c, wsOpts)
case "http":
host, _, _ := net.SplitHostPort(v.addr)
httpOpts := &vmess.HTTPConfig{
Host: host,
Method: v.option.HTTPOpts.Method,
Path: v.option.HTTPOpts.Path,
Headers: v.option.HTTPOpts.Headers,
}

c, err = vmess.StreamHTTPConn(c, httpOpts), nil
}

if err != nil {
return nil, err
}

return v.client.StreamConn(c, parseVmessAddr(metadata))
}

func (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {
Expand Down Expand Up @@ -66,7 +115,7 @@ func (v *Vmess) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
return nil, fmt.Errorf("%s connect error", v.addr)
}
tcpKeepAlive(c)
c, err = v.client.New(c, parseVmessAddr(metadata))
c, err = v.StreamConn(c, metadata)
if err != nil {
return nil, fmt.Errorf("new vmess client error: %v", err)
}
Expand All @@ -76,17 +125,11 @@ func (v *Vmess) DialUDP(metadata *C.Metadata) (C.PacketConn, error) {
func NewVmess(option VmessOption) (*Vmess, error) {
security := strings.ToLower(option.Cipher)
client, err := vmess.NewClient(vmess.Config{
UUID: option.UUID,
AlterID: uint16(option.AlterID),
Security: security,
TLS: option.TLS,
HostName: option.Server,
Port: strconv.Itoa(option.Port),
NetWork: option.Network,
WebSocketPath: option.WSPath,
WebSocketHeaders: option.WSHeaders,
SkipCertVerify: option.SkipCertVerify,
SessionCache: getClientSessionCache(),
UUID: option.UUID,
AlterID: uint16(option.AlterID),
Security: security,
HostName: option.Server,
Port: strconv.Itoa(option.Port),
})
if err != nil {
return nil, err
Expand All @@ -100,6 +143,7 @@ func NewVmess(option VmessOption) (*Vmess, error) {
udp: true,
},
client: client,
option: &option,
}, nil
}

Expand Down
27 changes: 16 additions & 11 deletions component/v2ray-plugin/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import (

// Option is options of websocket obfs
type Option struct {
Host string
Path string
Headers map[string]string
TLSConfig *tls.Config
Mux bool
Host string
Port string
Path string
Headers map[string]string
TLS bool
SkipCertVerify bool
SessionCache tls.ClientSessionCache
Mux bool
}

// NewV2rayObfs return a HTTPObfs
Expand All @@ -25,15 +28,17 @@ func NewV2rayObfs(conn net.Conn, option *Option) (net.Conn, error) {
}

config := &vmess.WebsocketConfig{
Host: option.Host,
Path: option.Path,
TLS: option.TLSConfig != nil,
Headers: header,
TLSConfig: option.TLSConfig,
Host: option.Host,
Port: option.Port,
Path: option.Path,
TLS: option.TLS,
Headers: header,
SkipCertVerify: option.SkipCertVerify,
SessionCache: option.SessionCache,
}

var err error
conn, err = vmess.NewWebsocketConn(conn, config)
conn, err = vmess.StreamWebsocketConn(conn, config)
if err != nil {
return nil, err
}
Expand Down
76 changes: 76 additions & 0 deletions component/vmess/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package vmess

import (
"bytes"
"fmt"
"math/rand"
"net"
"net/http"
"net/textproto"
)

type httpConn struct {
net.Conn
cfg *HTTPConfig
rhandshake bool
whandshake bool
}

type HTTPConfig struct {
Method string
Host string
Path []string
Headers map[string][]string
}

// Read implements net.Conn.Read()
func (hc *httpConn) Read(b []byte) (int, error) {
if hc.rhandshake {
n, err := hc.Conn.Read(b)
return n, err
}

reader := textproto.NewConn(hc.Conn)
// First line: GET /index.html HTTP/1.0
if _, err := reader.ReadLine(); err != nil {
return 0, err
}

if _, err := reader.ReadMIMEHeader(); err != nil {
return 0, err
}

hc.rhandshake = true
return hc.Conn.Read(b)
}

// Write implements io.Writer.
func (hc *httpConn) Write(b []byte) (int, error) {
if hc.whandshake {
return hc.Conn.Write(b)
}

path := hc.cfg.Path[rand.Intn(len(hc.cfg.Path))]
u := fmt.Sprintf("http://%s%s", hc.cfg.Host, path)
req, _ := http.NewRequest("GET", u, bytes.NewBuffer(b))
for key, list := range hc.cfg.Headers {
req.Header.Set(key, list[rand.Intn(len(list))])
}
req.ContentLength = int64(len(b))
if err := req.Write(hc.Conn); err != nil {
return 0, err
}
hc.whandshake = true
return len(b), nil
}

func (hc *httpConn) Close() error {
return hc.Conn.Close()
}

func StreamHTTPConn(conn net.Conn, cfg *HTTPConfig) net.Conn {
return &httpConn{
Conn: conn,
cfg: cfg,
}
}
Loading

0 comments on commit 19f809b

Please sign in to comment.