Commit e476a9f7 authored by rui.zheng's avatar rui.zheng

add vendor

parent 15a5d74b
The MIT License (MIT)
Copyright (c) 2014 Coda Hale
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
chacha20
========
[![Build Status](https://travis-ci.org/codahale/chacha20.png?branch=master)](https://travis-ci.org/codahale/chacha20)
A pure Go implementation of the ChaCha20 stream cipher.
For documentation, check [godoc](http://godoc.org/github.com/codahale/chacha20).
// Package chacha20 provides a pure Go implementation of ChaCha20, a fast,
// secure stream cipher.
//
// From Bernstein, Daniel J. "ChaCha, a variant of Salsa20." Workshop Record of
// SASC. 2008. (http://cr.yp.to/chacha/chacha-20080128.pdf):
//
// ChaCha8 is a 256-bit stream cipher based on the 8-round cipher Salsa20/8.
// The changes from Salsa20/8 to ChaCha8 are designed to improve diffusion per
// round, conjecturally increasing resistance to cryptanalysis, while
// preserving -- and often improving -- time per round. ChaCha12 and ChaCha20
// are analogous modifications of the 12-round and 20-round ciphers Salsa20/12
// and Salsa20/20. This paper presents the ChaCha family and explains the
// differences between Salsa20 and ChaCha.
//
// For more information, see http://cr.yp.to/chacha.html
package chacha20
import (
"crypto/cipher"
"encoding/binary"
"errors"
"unsafe"
)
const (
// KeySize is the length of ChaCha20 keys, in bytes.
KeySize = 32
// NonceSize is the length of ChaCha20 nonces, in bytes.
NonceSize = 8
// XNonceSize is the length of XChaCha20 nonces, in bytes.
XNonceSize = 24
)
var (
// ErrInvalidKey is returned when the provided key is not 256 bits long.
ErrInvalidKey = errors.New("invalid key length (must be 256 bits)")
// ErrInvalidNonce is returned when the provided nonce is not 64 bits long.
ErrInvalidNonce = errors.New("invalid nonce length (must be 64 bits)")
// ErrInvalidXNonce is returned when the provided nonce is not 192 bits
// long.
ErrInvalidXNonce = errors.New("invalid nonce length (must be 192 bits)")
// ErrInvalidRounds is returned when the provided rounds is not
// 8, 12, or 20.
ErrInvalidRounds = errors.New("invalid rounds number (must be 8, 12, or 20)")
)
// New creates and returns a new cipher.Stream. The key argument must be 256
// bits long, and the nonce argument must be 64 bits long. The nonce must be
// randomly generated or used only once. This Stream instance must not be used
// to encrypt more than 2^70 bytes (~1 zettabyte).
func New(key []byte, nonce []byte) (cipher.Stream, error) {
return NewWithRounds(key, nonce, 20)
}
// NewWithRounds creates and returns a new cipher.Stream just like New but
// the rounds number of 8, 12, or 20 can be specified.
func NewWithRounds(key []byte, nonce []byte, rounds uint8) (cipher.Stream, error) {
if len(key) != KeySize {
return nil, ErrInvalidKey
}
if len(nonce) != NonceSize {
return nil, ErrInvalidNonce
}
if (rounds != 8) && (rounds != 12) && (rounds != 20) {
return nil, ErrInvalidRounds
}
s := new(stream)
s.init(key, nonce, rounds)
s.advance()
return s, nil
}
// NewXChaCha creates and returns a new cipher.Stream. The key argument must be
// 256 bits long, and the nonce argument must be 192 bits long. The nonce must
// be randomly generated or only used once. This Stream instance must not be
// used to encrypt more than 2^70 bytes (~1 zetta byte).
func NewXChaCha(key []byte, nonce []byte) (cipher.Stream, error) {
return NewXChaChaWithRounds(key, nonce, 20)
}
// NewXChaChaWithRounds creates and returns a new cipher.Stream just like
// NewXChaCha but the rounds number of 8, 12, or 20 can be specified.
func NewXChaChaWithRounds(key []byte, nonce []byte, rounds uint8) (cipher.Stream, error) {
if len(key) != KeySize {
return nil, ErrInvalidKey
}
if len(nonce) != XNonceSize {
return nil, ErrInvalidXNonce
}
if (rounds != 8) && (rounds != 12) && (rounds != 20) {
return nil, ErrInvalidRounds
}
s := new(stream)
s.init(key, nonce, rounds)
// Call HChaCha to derive the subkey using the key and the first 16 bytes
// of the nonce, and re-initialize the state using the subkey and the
// remaining nonce.
blockArr := (*[stateSize]uint32)(unsafe.Pointer(&s.block))
core(&s.state, blockArr, s.rounds, true)
copy(s.state[4:8], blockArr[0:4])
copy(s.state[8:12], blockArr[12:16])
s.state[12] = 0
s.state[13] = 0
s.state[14] = binary.LittleEndian.Uint32(nonce[16:])
s.state[15] = binary.LittleEndian.Uint32(nonce[20:])
s.advance()
return s, nil
}
type stream struct {
state [stateSize]uint32 // the state as an array of 16 32-bit words
block [blockSize]byte // the keystream as an array of 64 bytes
offset int // the offset of used bytes in block
rounds uint8
}
func (s *stream) XORKeyStream(dst, src []byte) {
// Stride over the input in 64-byte blocks, minus the amount of keystream
// previously used. This will produce best results when processing blocks
// of a size evenly divisible by 64.
i := 0
max := len(src)
for i < max {
gap := blockSize - s.offset
limit := i + gap
if limit > max {
limit = max
}
o := s.offset
for j := i; j < limit; j++ {
dst[j] = src[j] ^ s.block[o]
o++
}
i += gap
s.offset = o
if o == blockSize {
s.advance()
}
}
}
func (s *stream) init(key []byte, nonce []byte, rounds uint8) {
// the magic constants for 256-bit keys
s.state[0] = 0x61707865
s.state[1] = 0x3320646e
s.state[2] = 0x79622d32
s.state[3] = 0x6b206574
s.state[4] = binary.LittleEndian.Uint32(key[0:])
s.state[5] = binary.LittleEndian.Uint32(key[4:])
s.state[6] = binary.LittleEndian.Uint32(key[8:])
s.state[7] = binary.LittleEndian.Uint32(key[12:])
s.state[8] = binary.LittleEndian.Uint32(key[16:])
s.state[9] = binary.LittleEndian.Uint32(key[20:])
s.state[10] = binary.LittleEndian.Uint32(key[24:])
s.state[11] = binary.LittleEndian.Uint32(key[28:])
switch len(nonce) {
case NonceSize:
// ChaCha20 uses 8 byte nonces.
s.state[12] = 0
s.state[13] = 0
s.state[14] = binary.LittleEndian.Uint32(nonce[0:])
s.state[15] = binary.LittleEndian.Uint32(nonce[4:])
case XNonceSize:
// XChaCha20 derives the subkey via HChaCha initialized
// with the first 16 bytes of the nonce.
s.state[12] = binary.LittleEndian.Uint32(nonce[0:])
s.state[13] = binary.LittleEndian.Uint32(nonce[4:])
s.state[14] = binary.LittleEndian.Uint32(nonce[8:])
s.state[15] = binary.LittleEndian.Uint32(nonce[12:])
default:
// Never happens, both ctors validate the nonce length.
panic("invalid nonce size")
}
s.rounds = rounds
}
// BUG(codahale): Totally untested on big-endian CPUs. Would very much
// appreciate someone with an ARM device giving this a swing.
// advances the keystream
func (s *stream) advance() {
core(&s.state, (*[stateSize]uint32)(unsafe.Pointer(&s.block)), s.rounds, false)
if bigEndian {
j := blockSize - 1
for i := 0; i < blockSize/2; i++ {
s.block[j], s.block[i] = s.block[i], s.block[j]
j--
}
}
s.offset = 0
i := s.state[12] + 1
s.state[12] = i
if i == 0 {
s.state[13]++
}
}
const (
wordSize = 4 // the size of ChaCha20's words
stateSize = 16 // the size of ChaCha20's state, in words
blockSize = stateSize * wordSize // the size of ChaCha20's block, in bytes
)
var (
bigEndian bool // whether or not we're running on a bigEndian CPU
)
// Do some up-front bookkeeping on what sort of CPU we're using. ChaCha20 treats
// its state as a little-endian byte array when it comes to generating the
// keystream, which allows for a zero-copy approach to the core transform. On
// big-endian architectures, we have to take a hit to reverse the bytes.
func init() {
x := uint32(0x04030201)
y := [4]byte{0x1, 0x2, 0x3, 0x4}
bigEndian = *(*[4]byte)(unsafe.Pointer(&x)) != y
}
// The ChaCha20 core transform.
// An unrolled and inlined implementation in pure Go.
package chacha20
func core(input, output *[stateSize]uint32, rounds uint8, hchacha bool) {
var (
x00 = input[0]
x01 = input[1]
x02 = input[2]
x03 = input[3]
x04 = input[4]
x05 = input[5]
x06 = input[6]
x07 = input[7]
x08 = input[8]
x09 = input[9]
x10 = input[10]
x11 = input[11]
x12 = input[12]
x13 = input[13]
x14 = input[14]
x15 = input[15]
)
var x uint32
// Unrolling all 20 rounds kills performance on modern Intel processors
// (Tested on a i5 Haswell, likely applies to Sandy Bridge+), due to uop
// cache thrashing. The straight forward 2 rounds per loop implementation
// of this has double the performance of the fully unrolled version.
for i := uint8(0); i < rounds; i += 2 {
x00 += x04
x = x12 ^ x00
x12 = (x << 16) | (x >> 16)
x08 += x12
x = x04 ^ x08
x04 = (x << 12) | (x >> 20)
x00 += x04
x = x12 ^ x00
x12 = (x << 8) | (x >> 24)
x08 += x12
x = x04 ^ x08
x04 = (x << 7) | (x >> 25)
x01 += x05
x = x13 ^ x01
x13 = (x << 16) | (x >> 16)
x09 += x13
x = x05 ^ x09
x05 = (x << 12) | (x >> 20)
x01 += x05
x = x13 ^ x01
x13 = (x << 8) | (x >> 24)
x09 += x13
x = x05 ^ x09
x05 = (x << 7) | (x >> 25)
x02 += x06
x = x14 ^ x02
x14 = (x << 16) | (x >> 16)
x10 += x14
x = x06 ^ x10
x06 = (x << 12) | (x >> 20)
x02 += x06
x = x14 ^ x02
x14 = (x << 8) | (x >> 24)
x10 += x14
x = x06 ^ x10
x06 = (x << 7) | (x >> 25)
x03 += x07
x = x15 ^ x03
x15 = (x << 16) | (x >> 16)
x11 += x15
x = x07 ^ x11
x07 = (x << 12) | (x >> 20)
x03 += x07
x = x15 ^ x03
x15 = (x << 8) | (x >> 24)
x11 += x15
x = x07 ^ x11
x07 = (x << 7) | (x >> 25)
x00 += x05
x = x15 ^ x00
x15 = (x << 16) | (x >> 16)
x10 += x15
x = x05 ^ x10
x05 = (x << 12) | (x >> 20)
x00 += x05
x = x15 ^ x00
x15 = (x << 8) | (x >> 24)
x10 += x15
x = x05 ^ x10
x05 = (x << 7) | (x >> 25)
x01 += x06
x = x12 ^ x01
x12 = (x << 16) | (x >> 16)
x11 += x12
x = x06 ^ x11
x06 = (x << 12) | (x >> 20)
x01 += x06
x = x12 ^ x01
x12 = (x << 8) | (x >> 24)
x11 += x12
x = x06 ^ x11
x06 = (x << 7) | (x >> 25)
x02 += x07
x = x13 ^ x02
x13 = (x << 16) | (x >> 16)
x08 += x13
x = x07 ^ x08
x07 = (x << 12) | (x >> 20)
x02 += x07
x = x13 ^ x02
x13 = (x << 8) | (x >> 24)
x08 += x13
x = x07 ^ x08
x07 = (x << 7) | (x >> 25)
x03 += x04
x = x14 ^ x03
x14 = (x << 16) | (x >> 16)
x09 += x14
x = x04 ^ x09
x04 = (x << 12) | (x >> 20)
x03 += x04
x = x14 ^ x03
x14 = (x << 8) | (x >> 24)
x09 += x14
x = x04 ^ x09
x04 = (x << 7) | (x >> 25)
}
if !hchacha {
output[0] = x00 + input[0]
output[1] = x01 + input[1]
output[2] = x02 + input[2]
output[3] = x03 + input[3]
output[4] = x04 + input[4]
output[5] = x05 + input[5]
output[6] = x06 + input[6]
output[7] = x07 + input[7]
output[8] = x08 + input[8]
output[9] = x09 + input[9]
output[10] = x10 + input[10]
output[11] = x11 + input[11]
output[12] = x12 + input[12]
output[13] = x13 + input[13]
output[14] = x14 + input[14]
output[15] = x15 + input[15]
} else {
output[0] = x00
output[1] = x01
output[2] = x02
output[3] = x03
output[4] = x04
output[5] = x05
output[6] = x06
output[7] = x07
output[8] = x08
output[9] = x09
output[10] = x10
output[11] = x11
output[12] = x12
output[13] = x13
output[14] = x14
output[15] = x15
}
}
The MIT License (MIT)
Copyright (c) 2014 郑锐
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
package gosocks5
import (
"io"
//"log"
"net"
"sync"
"time"
)
type Selector interface {
// return supported methods
Methods() []uint8
// select method
Select(methods ...uint8) (method uint8)
// on method selected
OnSelected(method uint8, conn net.Conn) (net.Conn, error)
}
type Conn struct {
c net.Conn
selector Selector
method uint8
isClient bool
handshaked bool
handshakeMutex sync.Mutex
handshakeErr error
}
func ClientConn(conn net.Conn, selector Selector) *Conn {
return &Conn{
c: conn,
selector: selector,
isClient: true,
}
}
func ServerConn(conn net.Conn, selector Selector) *Conn {
return &Conn{
c: conn,
selector: selector,
}
}
func (conn *Conn) Handleshake() error {
conn.handshakeMutex.Lock()
defer conn.handshakeMutex.Unlock()
if err := conn.handshakeErr; err != nil {
return err
}
if conn.handshaked {
return nil
}
if conn.isClient {
conn.handshakeErr = conn.clientHandshake()
} else {
conn.handshakeErr = conn.serverHandshake()
}
return conn.handshakeErr
}
func (conn *Conn) clientHandshake() error {
var methods []uint8
var nm int
if conn.selector != nil {
methods = conn.selector.Methods()
}
nm = len(methods)
if nm == 0 {
nm = 1
}
b := make([]byte, 2+nm)
b[0] = Ver5
b[1] = uint8(nm)
copy(b[2:], methods)
if _, err := conn.c.Write(b); err != nil {
return err
}
if _, err := io.ReadFull(conn.c, b[:2]); err != nil {
return err
}
if b[0] != Ver5 {
return ErrBadVersion
}
if conn.selector != nil {
c, err := conn.selector.OnSelected(b[1], conn.c)
if err != nil {
return err
}
conn.c = c
}
conn.method = b[1]
//log.Println("method:", conn.method)
conn.handshaked = true
return nil
}
func (conn *Conn) serverHandshake() error {
methods, err := ReadMethods(conn.c)
if err != nil {
return err
}
method := MethodNoAuth
if conn.selector != nil {
method = conn.selector.Select(methods...)
}
if _, err := conn.c.Write([]byte{Ver5, method}); err != nil {
return err
}
if conn.selector != nil {
c, err := conn.selector.OnSelected(method, conn.c)
if err != nil {
return err
}
conn.c = c
}
conn.method = method
//log.Println("method:", method)
conn.handshaked = true
return nil
}
func (conn *Conn) Read(b []byte) (n int, err error) {
if err = conn.Handleshake(); err != nil {
return
}
return conn.c.Read(b)
}
func (conn *Conn) Write(b []byte) (n int, err error) {
if err = conn.Handleshake(); err != nil {
return
}
return conn.c.Write(b)
}
func (conn *Conn) Close() error {
return conn.c.Close()
}
func (conn *Conn) LocalAddr() net.Addr {
return conn.c.LocalAddr()
}
func (conn *Conn) RemoteAddr() net.Addr {
return conn.c.RemoteAddr()
}
func (conn *Conn) SetDeadline(t time.Time) error {
return conn.c.SetDeadline(t)
}
func (conn *Conn) SetReadDeadline(t time.Time) error {
return conn.c.SetReadDeadline(t)
}
func (conn *Conn) SetWriteDeadline(t time.Time) error {
return conn.c.SetWriteDeadline(t)
}
This diff is collapsed.
Network Working Group M. Leech
Request for Comments: 1929 Bell-Northern Research Ltd
Category: Standards Track March 1996
Username/Password Authentication for SOCKS V5
Status of this Memo
This document specifies an Internet standards track protocol for the
Internet community, and requests discussion and suggestions for
improvements. Please refer to the current edition of the "Internet
Official Protocol Standards" (STD 1) for the standardization state
and status of this protocol. Distribution of this memo is unlimited.
1. Introduction
The protocol specification for SOCKS Version 5 specifies a
generalized framework for the use of arbitrary authentication
protocols in the initial socks connection setup. This document
describes one of those protocols, as it fits into the SOCKS Version 5
authentication "subnegotiation".
Note:
Unless otherwise noted, the decimal numbers appearing in packet-
format diagrams represent the length of the corresponding field, in
octets. Where a given octet must take on a specific value, the
syntax X'hh' is used to denote the value of the single octet in that
field. When the word 'Variable' is used, it indicates that the
corresponding field has a variable length defined either by an
associated (one or two octet) length field, or by a data type field.
2. Initial negotiation
Once the SOCKS V5 server has started, and the client has selected the
Username/Password Authentication protocol, the Username/Password
subnegotiation begins. This begins with the client producing a
Username/Password request:
+----+------+----------+------+----------+
|VER | ULEN | UNAME | PLEN | PASSWD |
+----+------+----------+------+----------+
| 1 | 1 | 1 to 255 | 1 | 1 to 255 |
+----+------+----------+------+----------+
Leech Standards Track [Page 1]
RFC 1929 Username Authentication for SOCKS V5 March 1996
The VER field contains the current version of the subnegotiation,
which is X'01'. The ULEN field contains the length of the UNAME field
that follows. The UNAME field contains the username as known to the
source operating system. The PLEN field contains the length of the
PASSWD field that follows. The PASSWD field contains the password
association with the given UNAME.
The server verifies the supplied UNAME and PASSWD, and sends the
following response:
+----+--------+
|VER | STATUS |
+----+--------+
| 1 | 1 |
+----+--------+
A STATUS field of X'00' indicates success. If the server returns a
`failure' (STATUS value other than X'00') status, it MUST close the
connection.
3. Security Considerations
This document describes a subnegotiation that provides authentication
services to the SOCKS protocol. Since the request carries the
password in cleartext, this subnegotiation is not recommended for
environments where "sniffing" is possible and practical.
4. Author's Address
Marcus Leech
Bell-Northern Research Ltd
P.O. Box 3511, Station C
Ottawa, ON
CANADA K1Y 4H7
Phone: +1 613 763 9145
EMail: mleech@bnr.ca
Leech Standards Track [Page 2]
This diff is collapsed.
MIT License
Copyright (c) 2016 ginuerzh
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
gost - GO Simple Tunnel
======
### GO语言实现的安全隧道
[English README](README_en.md)
特性
------
* 可同时监听多端口
* 可设置转发代理,支持多级转发(代理链)
* 支持标准HTTP/HTTPS/SOCKS5代理协议
* SOCKS5代理支持TLS协商加密
* Tunnel UDP over TCP
* 支持Shadowsocks协议 (OTA: 2.2+)
* 支持本地/远程端口转发 (2.1+)
* 支持HTTP 2.0 (2.2+)
* 实验性支持QUIC (2.3+)
* 支持KCP协议 (2.3+)
* 透明代理 (2.3+)
二进制文件下载:https://github.com/ginuerzh/gost/releases
Google讨论组: https://groups.google.com/d/forum/go-gost
在gost中,gost与其他代理服务都被看作是代理节点,gost可以自己处理请求,或者将请求转发给任意一个或多个代理节点。
参数说明
------
#### 代理及代理链
适用于-L和-F参数
```bash
[scheme://][user:pass@host]:port
```
scheme分为两部分: protocol+transport
protocol: 代理协议类型(http, socks5, shadowsocks), transport: 数据传输方式(ws, wss, tls, http2, quic, kcp), 二者可以任意组合,或单独使用:
> http - 作为HTTP代理: http://:8080
> http+tls - 作为HTTPS代理(可能需要提供受信任的证书): http+tls://:443
> http2 - 作为HTTP2代理并向下兼容HTTPS代理: http2://:443
> socks - 作为标准SOCKS5代理(支持tls协商加密): socks://:1080
> socks+wss - 作为SOCKS5代理,使用websocket传输数据: socks+wss://:1080
> tls - 作为HTTPS/SOCKS5代理,使用tls传输数据: tls://:443
> ss - 作为Shadowsocks服务,ss://aes-256-cfb:123456@:8338
> quic - 作为QUIC代理,quic://:6121
> kcp - 作为KCP代理,kcp://:8388或kcp://aes:123456@:8388
> redirect - 作为透明代理,redirect://:12345
#### 端口转发
适用于-L参数
```bash
scheme://[bind_address]:port/[host]:hostport
```
> scheme - 端口转发模式, 本地端口转发: tcp, udp; 远程端口转发: rtcp, rudp
> bind_address:port - 本地/远程绑定地址
> host:hostport - 目标访问地址
#### 配置文件
> -C : 指定配置文件路径
配置文件为标准json格式:
```json
{
"ServeNodes": [
":8080",
"ss://chacha20:12345678@:8338"
],
"ChainNodes": [
"http://192.168.1.1:8080",
"https://10.0.2.1:443"
]
}
```
ServeNodes等同于-L参数,ChainNodes等同于-F参数
#### 开启日志
> -logtostderr : 输出到控制台
> -v=3 : 日志级别(1-5),级别越高,日志越详细(级别5将开启http2 debug)
> -log_dir=/log/dir/path : 输出到目录/log/dir/path
使用方法
------
#### 不设置转发代理
<img src="https://ginuerzh.github.io/images/gost_01.png" />
* 作为标准HTTP/SOCKS5代理
```bash
gost -L=:8080
```
* 设置代理认证信息
```bash
gost -L=admin:123456@localhost:8080
```
* 多组认证信息
```bash
gost -L=localhost:8080?secrets=secrets.txt
```
通过secrets参数可以为HTTP/SOCKS5代理设置多组认证信息,格式为:
```plain
# username password
test001 123456
test002 12345678
```
* 多端口监听
```bash
gost -L=http2://:443 -L=socks://:1080 -L=ss://aes-128-cfb:123456@:8338
```
#### 设置转发代理
<img src="https://ginuerzh.github.io/images/gost_02.png" />
```bash
gost -L=:8080 -F=192.168.1.1:8081
```
* 转发代理认证
```bash
gost -L=:8080 -F=http://admin:123456@192.168.1.1:8081
```
#### 设置多级转发代理(代理链)
<img src="https://ginuerzh.github.io/images/gost_03.png" />
```bash
gost -L=:8080 -F=http+tls://192.168.1.1:443 -F=socks+ws://192.168.1.2:1080 -F=ss://aes-128-cfb:123456@192.168.1.3:8338 -F=a.b.c.d:NNNN
```
gost按照-F设置的顺序通过代理链将请求最终转发给a.b.c.d:NNNN处理,每一个转发代理可以是任意HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks类型代理。
#### 本地端口转发(TCP)
```bash
gost -L=tcp://:2222/192.168.1.1:22 -F=...
```
将本地TCP端口2222上的数据(通过代理链)转发到192.168.1.1:22上。
#### 本地端口转发(UDP)
```bash
gost -L=udp://:5353/192.168.1.1:53 -F=...
```
将本地UDP端口5353上的数据(通过代理链)转发到192.168.1.1:53上。
**注:** 转发UDP数据时,如果有代理链,则代理链的末端(最后一个-F参数)必须是gost SOCKS5类型代理。
#### 远程端口转发(TCP)
```bash
gost -L=rtcp://:2222/192.168.1.1:22 -F=... -F=socks://172.24.10.1:1080
```
将172.24.10.1:2222上的数据(通过代理链)转发到192.168.1.1:22上。
#### 远程端口转发(UDP)
```bash
gost -L=rudp://:5353/192.168.1.1:53 -F=... -F=socks://172.24.10.1:1080
```
将172.24.10.1:5353上的数据(通过代理链)转发到192.168.1.1:53上。
**注:** 若要使用远程端口转发功能,代理链不能为空(至少要设置一个-F参数),且代理链的末端(最后一个-F参数)必须是gost SOCKS5类型代理。
#### HTTP2
gost的HTTP2支持两种模式并自适应:
* 作为标准的HTTP2代理,并向下兼容HTTPS代理。
* 作为transport(类似于wss),传输其他协议。
**注:** gost的代理链仅支持一个HTTP2代理节点,采用就近原则,会将第一个遇到的HTTP2代理节点视为HTTP2代理,其他HTTP2代理节点则被视为HTTPS代理。
#### QUIC
gost对QUIC的支持是基于[quic-go](https://github.com/lucas-clemente/quic-go)库。
服务端:
```bash
gost -L=quic://:6121
```
客户端(Chrome):
```bash
chrome --enable-quic --proxy-server=quic://server_ip:6121
```
**注:** 由于Chrome自身的限制,目前只能通过QUIC访问HTTP网站,无法访问HTTPS网站。
#### KCP
gost对KCP的支持是基于[kcp-go](https://github.com/xtaci/kcp-go)[kcptun](https://github.com/xtaci/kcptun)库。
服务端:
```bash
gost -L=kcp://:8388
```
客户端:
```bash
gost -L=:8080 -F=kcp://server_ip:8388
```
或者手动指定加密方法和密码(手动指定的加密方法和密码会覆盖配置文件中的相应值)
服务端:
```bash
gost -L=kcp://aes:123456@:8388
```
客户端:
```bash
gost -L=:8080 -F=kcp://aes:123456@server_ip:8388
```
gost会自动加载当前工作目录中的kcp.json(如果存在)配置文件,或者可以手动通过参数指定配置文件路径:
```bash
gost -L=kcp://:8388?c=/path/to/conf/file
```
**注:** 客户端若要开启KCP转发,当且仅当代理链不为空且首个代理节点(第一个-F参数)为kcp类型。
当KCP转发开启,代理链中的其他代理节点将被忽略。
#### 透明代理
基于iptables的透明代理。
```bash
gost -L=redirect://:12345 -F=http2://server_ip:443
```
加密机制
------
#### HTTP
对于HTTP可以使用TLS加密整个通讯过程,即HTTPS代理:
服务端:
```bash
gost -L=http+tls://:443
```
客户端:
```bash
gost -L=:8080 -F=http+tls://server_ip:443
```
#### HTTP2
gost仅支持使用TLS加密的HTTP2协议,不支持明文HTTP2传输。
服务端:
```bash
gost -L=http2://:443
```
客户端:
```bash
gost -L=:8080 -F=http2://server_ip:443
```
#### SOCKS5
gost支持标准SOCKS5协议的no-auth(0x00)和user/pass(0x02)方法,并在此基础上扩展了两个:tls(0x80)和tls-auth(0x82),用于数据加密。
服务端:
```bash
gost -L=socks://:1080
```
客户端:
```bash
gost -L=:8080 -F=socks://server_ip:1080
```
如果两端都是gost(如上)则数据传输会被加密(协商使用tls或tls-auth方法),否则使用标准SOCKS5进行通讯(no-auth或user/pass方法)。
**注:** 如果transport已经支持加密(wss, tls, http2),则SOCKS5不会再使用加密方法,防止不必要的双重加密。
#### Shadowsocks
gost对shadowsocks的支持是基于[shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go)库。
服务端(可以通过ota参数开启OTA模式):
```bash
gost -L=ss://aes-128-cfb:123456@:8338?ota=1
```
客户端:
```bash
gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338
```
#### TLS
gost内置了TLS证书,如果需要使用其他TLS证书,有两种方法:
* 在gost运行目录放置cert.pem(公钥)和key.pem(私钥)两个文件即可,gost会自动加载运行目录下的cert.pem和key.pem文件。
* 使用参数指定证书文件路径:
```bash
gost -L="http2://:443?cert=/path/to/my/cert/file&key=/path/to/my/key/file"
```
SOCKS5 UDP数据处理
------
#### 不设置转发代理
<img src="https://ginuerzh.github.io/images/udp01.png" height=100 />
gost作为标准SOCKS5代理处理UDP数据
#### 设置转发代理
<img src="https://ginuerzh.github.io/images/udp02.png" height=100 />
#### 设置多个转发代理(代理链)
<img src="https://ginuerzh.github.io/images/udp03.png" height=200 />
当设置转发代理时,gost会使用UDP-over-TCP方式转发UDP数据。proxy1 - proxyN可以为任意HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks类型代理。
限制条件
------
代理链中的HTTP代理节点必须支持CONNECT方法。
如果要转发SOCKS5的BIND和UDP请求,代理链的末端(最后一个-F参数)必须支持gost SOCKS5类型代理。
gost - GO Simple Tunnel
======
### A simple security tunnel written in Golang
Features
------
* Listening on multiple ports
* Multi-level forward proxy - proxy chain
* Standard HTTP/HTTPS/SOCKS5 proxy protocols support
* TLS encryption via negotiation support for SOCKS5 proxy
* Tunnel UDP over TCP
* Shadowsocks protocol support (OTA: 2.2+)
* Local/remote port forwarding (2.1+)
* HTTP 2.0 support (2.2+)
* Experimental QUIC support (2.3+)
* KCP protocol support (2.3+)
* Transparent proxy (2.3+)
Binary file download:https://github.com/ginuerzh/gost/releases
Google group: https://groups.google.com/d/forum/go-gost
Gost and other proxy services are considered to be proxy nodes,
gost can handle the request itself, or forward the request to any one or more proxy nodes.
Parameter Description
------
#### Proxy and proxy chain
Effective for the -L and -F parameters
```bash
[scheme://][user:pass@host]:port
```
scheme can be divided into two parts: protocol+transport
protocol: proxy protocol types (http, socks5, shadowsocks),
transport: data transmission mode (ws, wss, tls, http2, quic, kcp), may be used in any combination or individually:
> http - standard HTTP proxy: http://:8080
> http+tls - standard HTTPS proxy (may need to provide a trusted certificate): http+tls://:443
> http2 - HTTP2 proxy and backwards-compatible with HTTPS proxy: http2://:443
> socks - standard SOCKS5 proxy: socks://:1080
> socks+wss - SOCKS5 over websocket: socks+wss://:1080
> tls - HTTPS/SOCKS5 over tls: tls://:443
> ss - standard shadowsocks proxy, ss://aes-256-cfb:123456@:8338
> quic - standard QUIC proxy, quic://:6121
> kcp - standard KCP tunnel,kcp://:8388 or kcp://aes:123456@:8388
> redirect - transparent proxy,redirect://:12345
#### Port forwarding
Effective for the -L parameter
```bash
scheme://[bind_address]:port/[host]:hostport
```
> scheme - forward mode, local: tcp, udp; remote: rtcp, rudp
> bind_address:port - local/remote binding address
> host:hostport - target address
#### Configuration file
> -C : specifies the configuration file path
The configuration file is in standard JSON format:
```json
{
"ServeNodes": [
":8080",
"ss://chacha20:12345678@:8338"
],
"ChainNodes": [
"http://192.168.1.1:8080",
"https://10.0.2.1:443"
]
}
```
ServeNodes is equivalent to the -L parameter, ChainNodes is equivalent to the -F parameter.
#### Logging
> -logtostderr : log to console
> -v=3 : log level (1-5),The higher the level, the more detailed the log (level 5 will enable HTTP2 debug)
> -log_dir=/log/dir/path : log to directory /log/dir/path
Usage
------
#### No forward proxy
<img src="https://ginuerzh.github.io/images/gost_01.png" />
* Standard HTTP/SOCKS5 proxy
```bash
gost -L=:8080
```
* Proxy authentication
```bash
gost -L=admin:123456@localhost:8080
```
* Multiple sets of authentication information
```bash
gost -L=localhost:8080?secrets=secrets.txt
```
The secrets parameter allows you to set multiple authentication information for HTTP/SOCKS5 proxies, the format is:
```plain
# username password
test001 123456
test002 12345678
```
* Listen on multiple ports
```bash
gost -L=http2://:443 -L=socks://:1080 -L=ss://aes-128-cfb:123456@:8338
```
#### Forward proxy
<img src="https://ginuerzh.github.io/images/gost_02.png" />
```bash
gost -L=:8080 -F=192.168.1.1:8081
```
* Forward proxy authentication
```bash
gost -L=:8080 -F=http://admin:123456@192.168.1.1:8081
```
#### Multi-level forward proxy
<img src="https://ginuerzh.github.io/images/gost_03.png" />
```bash
gost -L=:8080 -F=http+tls://192.168.1.1:443 -F=socks+ws://192.168.1.2:1080 -F=ss://aes-128-cfb:123456@192.168.1.3:8338 -F=a.b.c.d:NNNN
```
Gost forwards the request to a.b.c.d:NNNN through the proxy chain in the order set by -F,
each forward proxy can be any HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks type.
#### Local TCP port forwarding
```bash
gost -L=tcp://:2222/192.168.1.1:22 -F=...
```
The data on the local TCP port 2222 is forwarded to 192.168.1.1:22 (through the proxy chain).
#### Local UDP port forwarding
```bash
gost -L=udp://:5353/192.168.1.1:53 -F=...
```
The data on the local UDP port 5353 is forwarded to 192.168.1.1:53 (through the proxy chain).
**NOTE:** When forwarding UDP data, if there is a proxy chain, the end of the chain (the last -F parameter) must be gost SOCKS5 proxy.
#### Remote TCP port forwarding
```bash
gost -L=rtcp://:2222/192.168.1.1:22 -F=... -F=socks://172.24.10.1:1080
```
The data on 172.24.10.1:2222 is forwarded to 192.168.1.1:22 (through the proxy chain).
#### Remote UDP port forwarding
```bash
gost -L=rudp://:5353/192.168.1.1:53 -F=... -F=socks://172.24.10.1:1080
```
The data on 172.24.10.1:5353 is forwarded to 192.168.1.1:53 (through the proxy chain).
**NOTE:** To use the remote port forwarding feature, the proxy chain can not be empty (at least one -F parameter is set)
and the end of the chain (last -F parameter) must be gost SOCKS5 proxy.
#### HTTP2
Gost HTTP2 supports two modes and self-adapting:
* As a standard HTTP2 proxy, and backwards-compatible with the HTTPS proxy.
* As transport (similar to wss), tunnel other protocol.
**NOTE:** The proxy chain of gost supports only one HTTP2 proxy node and the nearest rule applies,
the first HTTP2 proxy node is treated as an HTTP2 proxy, and the other HTTP2 proxy nodes are treated as HTTPS proxies.
#### QUIC
Support for QUIC is based on library [quic-go](https://github.com/lucas-clemente/quic-go).
Server:
```bash
gost -L=quic://:6121
```
Client(Chrome):
```bash
chrome --enable-quic --proxy-server=quic://server_ip:6121
```
**NOTE:** Due to Chrome's limitations, it is currently only possible to access the HTTP (but not HTTPS) site through QUIC.
#### KCP
Support for KCP is based on libraries [kcp-go](https://github.com/xtaci/kcp-go) and [kcptun](https://github.com/xtaci/kcptun).
Server:
```bash
gost -L=kcp://:8388
```
Client:
```bash
gost -L=:8080 -F=kcp://server_ip:8388
```
Or manually specify the encryption method and password (Manually specifying the encryption method and password overwrites the corresponding value in the configuration file)
Server:
```bash
gost -L=kcp://aes:123456@:8388
```
Client:
```bash
gost -L=:8080 -F=kcp://aes:123456@server_ip:8388
```
Gost will automatically load kcp.json configuration file from current working directory if exists,
or you can use the parameter to specify the path to the file.
```bash
gost -L=kcp://:8388?c=/path/to/conf/file
```
**NOTE:** KCP will be enabled if and only if the proxy chain is not empty and the first proxy node (the first -F parameter) is of type KCP.
When KCP is enabled, other proxy nodes are ignored.
#### Transparent proxy
Iptables-based transparent proxy
```bash
gost -L=redirect://:12345 -F=http2://server_ip:443
```
Encryption Mechanism
------
#### HTTP
For HTTP, you can use TLS to encrypt the entire communication process, the HTTPS proxy:
Server:
```bash
gost -L=http+tls://:443
```
Client:
```bash
gost -L=:8080 -F=http+tls://server_ip:443
```
#### HTTP2
Gost supports only the HTTP2 protocol that uses TLS encryption (h2) and does not support plaintext HTTP2 (h2c) transport.
Server:
```bash
gost -L=http2://:443
```
Client:
```bash
gost -L=:8080 -F=http2://server_ip:443
```
#### SOCKS5
Gost supports the standard SOCKS5 protocol methods: no-auth (0x00) and user/pass (0x02),
and extends two methods for data encryption: tls(0x80) and tls-auth(0x82).
Server:
```bash
gost -L=socks://:1080
```
Client:
```bash
gost -L=:8080 -F=socks://server_ip:1080
```
If both ends are gosts (as example above), the data transfer will be encrypted (using tls or tls-auth).
Otherwise, use standard SOCKS5 for communication (no-auth or user/pass).
**NOTE:** If transport already supports encryption (wss, tls, http2), SOCKS5 will no longer use the encryption method to prevent unnecessary double encryption.
#### Shadowsocks
Support for shadowsocks is based on library [shadowsocks-go](https://github.com/shadowsocks/shadowsocks-go).
Server (The OTA mode can be enabled by the ota parameter):
```bash
gost -L=ss://aes-128-cfb:123456@:8338?ota=1
```
Client:
```bash
gost -L=:8080 -F=ss://aes-128-cfb:123456@server_ip:8338
```
#### TLS
There is built-in TLS certificate in gost, if you need to use other TLS certificate, there are two ways:
* Place two files cert.pem (public key) and key.pem (private key) in the current working directory, gost will automatically load them.
* Use the parameter to specify the path to the certificate file:
```bash
gost -L="http2://:443?cert=/path/to/my/cert/file&key=/path/to/my/key/file"
```
SOCKS5 UDP Data Processing
------
#### No forward proxy
<img src="https://ginuerzh.github.io/images/udp01.png" height=100 />
Gost acts as the standard SOCKS5 proxy for UDP relay.
#### Forward proxy
<img src="https://ginuerzh.github.io/images/udp02.png" height=100 />
#### Multi-level forward proxy
<img src="https://ginuerzh.github.io/images/udp03.png" height=200 />
When forward proxies are set, gost uses UDP-over-TCP to forward UDP data, proxy1 to proxyN can be any HTTP/HTTPS/HTTP2/SOCKS5/Shadowsocks type.
Limitation
------
The HTTP proxy node in the proxy chain must support the CONNECT method.
If the BIND and UDP requests for SOCKS5 are to be forwarded, the end of the chain (the last -F parameter) must be the gost SOCKS5 proxy.
package gost
import (
"crypto/tls"
"encoding/base64"
"errors"
"github.com/golang/glog"
"golang.org/x/net/http2"
"io"
//"io/ioutil"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strings"
"sync"
)
// Proxy chain holds a list of proxy nodes
type ProxyChain struct {
nodes []ProxyNode
lastNode *ProxyNode
http2NodeIndex int
http2Enabled bool
http2Client *http.Client
kcpEnabled bool
kcpConfig *KCPConfig
kcpSession *KCPSession
kcpMutex sync.Mutex
}
func NewProxyChain(nodes ...ProxyNode) *ProxyChain {
chain := &ProxyChain{nodes: nodes, http2NodeIndex: -1}
return chain
}
func (c *ProxyChain) AddProxyNode(node ...ProxyNode) {
c.nodes = append(c.nodes, node...)
}
func (c *ProxyChain) AddProxyNodeString(snode ...string) error {
for _, sn := range snode {
node, err := ParseProxyNode(sn)
if err != nil {
return err
}
c.AddProxyNode(node)
}
return nil
}
func (c *ProxyChain) Nodes() []ProxyNode {
return c.nodes
}
func (c *ProxyChain) GetNode(index int) *ProxyNode {
if index < len(c.nodes) {
return &c.nodes[index]
}
return nil
}
func (c *ProxyChain) SetNode(index int, node ProxyNode) {
if index < len(c.nodes) {
c.nodes[index] = node
}
}
// Init initialize the proxy chain.
// KCP will be enabled if the first proxy node is KCP proxy (transport == kcp), the remaining nodes are ignored.
// HTTP2 will be enabled when at least one HTTP2 proxy node (scheme == http2) is present.
//
// NOTE: Should be called immediately when proxy nodes are ready.
func (c *ProxyChain) Init() {
length := len(c.nodes)
if length == 0 {
return
}
c.lastNode = &c.nodes[length-1]
if c.nodes[0].Transport == "kcp" {
glog.V(LINFO).Infoln("KCP is enabled")
c.kcpEnabled = true
config, err := ParseKCPConfig(c.nodes[0].Get("c"))
if err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
}
if config == nil {
config = DefaultKCPConfig
}
if c.nodes[0].Users != nil {
config.Crypt = c.nodes[0].Users[0].Username()
config.Key, _ = c.nodes[0].Users[0].Password()
}
c.kcpConfig = config
return
}
// HTTP2 restrict: HTTP2 will be enabled when at least one HTTP2 proxy node is present.
for i, node := range c.nodes {
if node.Transport == "http2" {
glog.V(LINFO).Infoln("HTTP2 is enabled")
cfg := &tls.Config{
InsecureSkipVerify: node.insecureSkipVerify(),
ServerName: node.serverName,
}
c.http2NodeIndex = i
c.initHttp2Client(cfg, c.nodes[:i]...)
break // shortest chain for HTTP2
}
}
}
func (c *ProxyChain) KCPEnabled() bool {
return c.kcpEnabled
}
func (c *ProxyChain) Http2Enabled() bool {
return c.http2Enabled
}
func (c *ProxyChain) initHttp2Client(config *tls.Config, nodes ...ProxyNode) {
if c.http2NodeIndex < 0 || c.http2NodeIndex >= len(c.nodes) {
return
}
http2Node := c.nodes[c.http2NodeIndex]
tr := http2.Transport{
TLSClientConfig: config,
DialTLS: func(network, addr string, cfg *tls.Config) (net.Conn, error) {
// replace the default dialer with our proxy chain.
conn, err := c.dialWithNodes(false, http2Node.Addr, nodes...)
if err != nil {
return conn, err
}
return tls.Client(conn, cfg), nil
},
}
c.http2Client = &http.Client{Transport: &tr}
c.http2Enabled = true
}
// Connect to addr through proxy chain
func (c *ProxyChain) Dial(addr string) (net.Conn, error) {
if !strings.Contains(addr, ":") {
addr += ":80"
}
return c.dialWithNodes(true, addr, c.nodes...)
}
// GetConn initializes a proxy chain connection,
// if no proxy nodes on this chain, it will return error
func (c *ProxyChain) GetConn() (net.Conn, error) {
nodes := c.nodes
if len(nodes) == 0 {
return nil, ErrEmptyChain
}
if c.KCPEnabled() {
kcpConn, err := c.getKCPConn()
if err != nil {
return nil, err
}
pc := NewProxyConn(kcpConn, c.nodes[0])
if err := pc.Handshake(); err != nil {
pc.Close()
return nil, err
}
return pc, nil
}
if c.Http2Enabled() {
nodes = nodes[c.http2NodeIndex+1:]
if len(nodes) == 0 {
header := make(http.Header)
header.Set("Proxy-Switch", "gost") // Flag header to indicate server to switch to HTTP2 transport mode
conn, err := c.getHttp2Conn(header)
if err != nil {
return nil, err
}
http2Node := c.nodes[c.http2NodeIndex]
if http2Node.Transport == "http2" {
http2Node.Transport = "h2"
}
if http2Node.Protocol == "http2" {
http2Node.Protocol = "socks5" // assume it as socks5 protocol, so we can do much more things.
}
pc := NewProxyConn(conn, http2Node)
if err := pc.Handshake(); err != nil {
conn.Close()
return nil, err
}
return pc, nil
}
}
return c.travelNodes(true, nodes...)
}
func (c *ProxyChain) dialWithNodes(withHttp2 bool, addr string, nodes ...ProxyNode) (conn net.Conn, err error) {
if len(nodes) == 0 {
return net.DialTimeout("tcp", addr, DialTimeout)
}
if c.KCPEnabled() {
kcpConn, err := c.getKCPConn()
if err != nil {
return nil, err
}
pc := NewProxyConn(kcpConn, nodes[0])
if err := pc.Handshake(); err != nil {
pc.Close()
return nil, err
}
if err := pc.Connect(addr); err != nil {
pc.Close()
return nil, err
}
return pc, nil
}
if withHttp2 && c.Http2Enabled() {
nodes = nodes[c.http2NodeIndex+1:]
if len(nodes) == 0 {
return c.http2Connect(addr)
}
}
pc, err := c.travelNodes(withHttp2, nodes...)
if err != nil {
return
}
if err = pc.Connect(addr); err != nil {
pc.Close()
return
}
conn = pc
return
}
func (c *ProxyChain) travelNodes(withHttp2 bool, nodes ...ProxyNode) (conn *ProxyConn, err error) {
defer func() {
if err != nil && conn != nil {
conn.Close()
conn = nil
}
}()
var cc net.Conn
node := nodes[0]
if withHttp2 && c.Http2Enabled() {
cc, err = c.http2Connect(node.Addr)
} else {
cc, err = net.DialTimeout("tcp", node.Addr, DialTimeout)
}
if err != nil {
return
}
setKeepAlive(cc, KeepAliveTime)
pc := NewProxyConn(cc, node)
if err = pc.Handshake(); err != nil {
return
}
conn = pc
for _, node := range nodes[1:] {
if err = conn.Connect(node.Addr); err != nil {
return
}
pc := NewProxyConn(conn, node)
if err = pc.Handshake(); err != nil {
return
}
conn = pc
}
return
}
func (c *ProxyChain) initKCPSession() (err error) {
c.kcpMutex.Lock()
defer c.kcpMutex.Unlock()
if c.kcpSession == nil || c.kcpSession.IsClosed() {
glog.V(LINFO).Infoln("[kcp] new kcp session")
c.kcpSession, err = DialKCP(c.nodes[0].Addr, c.kcpConfig)
}
return
}
func (c *ProxyChain) getKCPConn() (conn net.Conn, err error) {
if !c.KCPEnabled() {
return nil, errors.New("KCP is not enabled")
}
if err = c.initKCPSession(); err != nil {
return nil, err
}
return c.kcpSession.GetConn()
}
// Initialize an HTTP2 transport if HTTP2 is enabled.
func (c *ProxyChain) getHttp2Conn(header http.Header) (net.Conn, error) {
if !c.Http2Enabled() {
return nil, errors.New("HTTP2 is not enabled")
}
http2Node := c.nodes[c.http2NodeIndex]
pr, pw := io.Pipe()
if header == nil {
header = make(http.Header)
}
req := http.Request{
Method: http.MethodConnect,
URL: &url.URL{Scheme: "https", Host: http2Node.Addr},
Header: header,
Proto: "HTTP/2.0",
ProtoMajor: 2,
ProtoMinor: 0,
Body: pr,
Host: http2Node.Addr,
ContentLength: -1,
}
if glog.V(LDEBUG) {
dump, _ := httputil.DumpRequest(&req, false)
glog.Infoln(string(dump))
}
resp, err := c.http2Client.Do(&req)
if err != nil {
return nil, err
}
if glog.V(LDEBUG) {
dump, _ := httputil.DumpResponse(resp, false)
glog.Infoln(string(dump))
}
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, errors.New(resp.Status)
}
conn := &http2Conn{r: resp.Body, w: pw}
conn.remoteAddr, _ = net.ResolveTCPAddr("tcp", http2Node.Addr)
return conn, nil
}
// Use HTTP2 as transport to connect target addr.
//
// BUG: SOCKS5 is ignored, only HTTP supported
func (c *ProxyChain) http2Connect(addr string) (net.Conn, error) {
if !c.Http2Enabled() {
return nil, errors.New("HTTP2 is not enabled")
}
http2Node := c.nodes[c.http2NodeIndex]
header := make(http.Header)
header.Set("Gost-Target", addr) // Flag header to indicate the address that server connected to
if http2Node.Users != nil {
header.Set("Proxy-Authorization",
"Basic "+base64.StdEncoding.EncodeToString([]byte(http2Node.Users[0].String())))
}
return c.getHttp2Conn(header)
}
package gost
import (
"bufio"
"bytes"
"crypto/tls"
"encoding/base64"
"errors"
"github.com/ginuerzh/gosocks5"
"github.com/golang/glog"
"github.com/shadowsocks/shadowsocks-go/shadowsocks"
"net"
"net/http"
"net/http/httputil"
"net/url"
"strconv"
"sync"
"time"
)
type ProxyConn struct {
conn net.Conn
Node ProxyNode
handshaked bool
handshakeMutex sync.Mutex
handshakeErr error
}
func NewProxyConn(conn net.Conn, node ProxyNode) *ProxyConn {
return &ProxyConn{
conn: conn,
Node: node,
}
}
// Handshake handshake with this proxy node based on the proxy node info: transport, protocol, authentication, etc.
//
// NOTE: any HTTP2 scheme will be treated as http (for protocol) or tls (for transport).
func (c *ProxyConn) Handshake() error {
c.handshakeMutex.Lock()
defer c.handshakeMutex.Unlock()
if err := c.handshakeErr; err != nil {
return err
}
if c.handshaked {
return nil
}
c.handshakeErr = c.handshake()
return c.handshakeErr
}
func (c *ProxyConn) handshake() error {
var tlsUsed bool
switch c.Node.Transport {
case "ws": // websocket connection
u := url.URL{Scheme: "ws", Host: c.Node.Addr, Path: "/ws"}
conn, err := WebsocketClientConn(u.String(), c.conn, nil)
if err != nil {
return err
}
c.conn = conn
case "wss": // websocket security
tlsUsed = true
u := url.URL{Scheme: "wss", Host: c.Node.Addr, Path: "/ws"}
config := &tls.Config{
InsecureSkipVerify: c.Node.insecureSkipVerify(),
ServerName: c.Node.serverName,
}
conn, err := WebsocketClientConn(u.String(), c.conn, config)
if err != nil {
return err
}
c.conn = conn
case "tls", "http2": // tls connection
tlsUsed = true
cfg := &tls.Config{
InsecureSkipVerify: c.Node.insecureSkipVerify(),
ServerName: c.Node.serverName,
}
c.conn = tls.Client(c.conn, cfg)
case "h2": // same as http2, but just set a flag for later using.
tlsUsed = true
case "kcp": // kcp connection
tlsUsed = true
default:
}
switch c.Node.Protocol {
case "socks", "socks5": // socks5 handshake with auth and tls supported
selector := &clientSelector{
methods: []uint8{
gosocks5.MethodNoAuth,
gosocks5.MethodUserPass,
//MethodTLS,
},
}
if len(c.Node.Users) > 0 {
selector.user = c.Node.Users[0]
}
if !tlsUsed { // if transport is not security, enable security socks5
selector.methods = append(selector.methods, MethodTLS)
selector.tlsConfig = &tls.Config{
InsecureSkipVerify: c.Node.insecureSkipVerify(),
ServerName: c.Node.serverName,
}
}
conn := gosocks5.ClientConn(c.conn, selector)
if err := conn.Handleshake(); err != nil {
return err
}
c.conn = conn
case "ss": // shadowsocks
if len(c.Node.Users) > 0 {
method := c.Node.Users[0].Username()
password, _ := c.Node.Users[0].Password()
cipher, err := shadowsocks.NewCipher(method, password)
if err != nil {
return err
}
c.conn = &shadowConn{conn: shadowsocks.NewConn(c.conn, cipher)}
}
case "http", "http2":
fallthrough
default:
}
c.handshaked = true
return nil
}
// Connect connect to addr through this proxy node
func (c *ProxyConn) Connect(addr string) error {
switch c.Node.Protocol {
case "ss": // shadowsocks
host, port, err := net.SplitHostPort(addr)
if err != nil {
return err
}
p, _ := strconv.Atoi(port)
req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{
Type: gosocks5.AddrDomain,
Host: host,
Port: uint16(p),
})
buf := bytes.Buffer{}
if err := req.Write(&buf); err != nil {
return err
}
b := buf.Bytes()
if _, err := c.Write(b[3:]); err != nil {
return err
}
glog.V(LDEBUG).Infoln("[ss]", req)
case "socks", "socks5":
host, port, err := net.SplitHostPort(addr)
if err != nil {
return err
}
p, _ := strconv.Atoi(port)
req := gosocks5.NewRequest(gosocks5.CmdConnect, &gosocks5.Addr{
Type: gosocks5.AddrDomain,
Host: host,
Port: uint16(p),
})
if err := req.Write(c); err != nil {
return err
}
glog.V(LDEBUG).Infoln("[socks5]", req)
reply, err := gosocks5.ReadReply(c)
if err != nil {
return err
}
glog.V(LDEBUG).Infoln("[socks5]", reply)
if reply.Rep != gosocks5.Succeeded {
return errors.New("Service unavailable")
}
case "http":
fallthrough
default:
req := &http.Request{
Method: http.MethodConnect,
URL: &url.URL{Host: addr},
Host: addr,
ProtoMajor: 1,
ProtoMinor: 1,
Header: make(http.Header),
}
req.Header.Set("Proxy-Connection", "keep-alive")
if len(c.Node.Users) > 0 {
user := c.Node.Users[0]
s := user.String()
if _, set := user.Password(); !set {
s += ":"
}
req.Header.Set("Proxy-Authorization",
"Basic "+base64.StdEncoding.EncodeToString([]byte(s)))
}
if err := req.Write(c); err != nil {
return err
}
if glog.V(LDEBUG) {
dump, _ := httputil.DumpRequest(req, false)
glog.Infoln(string(dump))
}
resp, err := http.ReadResponse(bufio.NewReader(c), req)
if err != nil {
return err
}
if glog.V(LDEBUG) {
dump, _ := httputil.DumpResponse(resp, false)
glog.Infoln(string(dump))
}
if resp.StatusCode != http.StatusOK {
return errors.New(resp.Status)
}
}
return nil
}
func (c *ProxyConn) Read(b []byte) (n int, err error) {
return c.conn.Read(b)
}
func (c *ProxyConn) Write(b []byte) (n int, err error) {
return c.conn.Write(b)
}
func (c *ProxyConn) Close() error {
return c.conn.Close()
}
func (c *ProxyConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *ProxyConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *ProxyConn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
func (c *ProxyConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *ProxyConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
This diff is collapsed.
package gost
import (
"crypto/tls"
"encoding/base64"
"errors"
"github.com/golang/glog"
"net"
"strings"
"time"
)
const (
Version = "2.3-rc1"
)
// Log level for glog
const (
LFATAL = iota
LERROR
LWARNING
LINFO
LDEBUG
)
var (
KeepAliveTime = 180 * time.Second
DialTimeout = 30 * time.Second
ReadTimeout = 90 * time.Second
WriteTimeout = 90 * time.Second
DefaultTTL = 60 // default udp node TTL in second for udp port forwarding
)
var (
SmallBufferSize = 1 * 1024 // 1KB small buffer
MediumBufferSize = 8 * 1024 // 8KB medium buffer
LargeBufferSize = 32 * 1024 // 32KB large buffer
)
var (
DefaultCertFile = "cert.pem"
DefaultKeyFile = "key.pem"
// This is the default cert and key data for convenience, providing your own cert is recommended.
defaultRawCert = []byte(`-----BEGIN CERTIFICATE-----
MIIC5jCCAdCgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
bzAeFw0xNDAzMTcwNjIwNTFaFw0xNTAzMTcwNjIwNTFaMBIxEDAOBgNVBAoTB0Fj
bWUgQ28wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDccNO1xmd4lWSf
d/0/QS3E93cYIWHw831i/IKxigdRD/XMZonLdEHywW6lOiXazaP8e6CqPGSmnl0x
5k/3dvGCMj2JCVxM6+z7NpL+AiwvXmvkj/TOciCgwqssCwYS2CiVwjfazRjx1ZUJ
VDC5qiyRsfktQ2fVHrpnJGVSRagmiQgwGWBilVG9B8QvRtpQKN/GQGq17oIQm8aK
kOdPt93g93ojMIg7YJpgDgOirvVz/hDn7YD4ryrtPos9CMafFkJprymKpRHyvz7P
8a3+OkuPjFjPnwOHQ5u1U3+8vC44vfb1ExWzDLoT8Xp8Gndx39k0f7MVOol3GnYu
MN/dvNUdAgMBAAGjSzBJMA4GA1UdDwEB/wQEAwIAoDATBgNVHSUEDDAKBggrBgEF
BQcDATAMBgNVHRMBAf8EAjAAMBQGA1UdEQQNMAuCCWxvY2FsaG9zdDALBgkqhkiG
9w0BAQUDggEBAIG8CJqvTIgJnNOK+i5/IUc/3yF/mSCWuG8qP+Fmo2t6T0PVOtc0
8wiWH5iWtCAhjn0MRY9l/hIjWm6gUZGHCGuEgsOPpJDYGoNLjH9Xwokm4y3LFNRK
UBrrrDbKRNibApBHCapPf6gC5sXcjOwx7P2/kiHDgY7YH47jfcRhtAPNsM4gjsEO
RmwENY+hRUFHIRfQTyalqND+x6PWhRo3K6hpHs4DQEYPq4P2kFPqUqSBymH+Ny5/
BcQ3wdMNmC6Bm/oiL1QV0M+/InOsAgQk/EDd0kmoU1ZT2lYHQduGmP099bOlHNpS
uqO3vXF3q8SPPr/A9TqSs7BKkBQbe0+cdsA=
-----END CERTIFICATE-----`)
defaultRawKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA3HDTtcZneJVkn3f9P0EtxPd3GCFh8PN9YvyCsYoHUQ/1zGaJ
y3RB8sFupTol2s2j/Hugqjxkpp5dMeZP93bxgjI9iQlcTOvs+zaS/gIsL15r5I/0
znIgoMKrLAsGEtgolcI32s0Y8dWVCVQwuaoskbH5LUNn1R66ZyRlUkWoJokIMBlg
YpVRvQfEL0baUCjfxkBqte6CEJvGipDnT7fd4Pd6IzCIO2CaYA4Doq71c/4Q5+2A
+K8q7T6LPQjGnxZCaa8piqUR8r8+z/Gt/jpLj4xYz58Dh0ObtVN/vLwuOL329RMV
swy6E/F6fBp3cd/ZNH+zFTqJdxp2LjDf3bzVHQIDAQABAoIBAHal26147nQ+pHwY
jxwers3XDCjWvup7g79lfcqlKi79UiUEA6KYHm7UogMYewt7p4nb2KwH+XycvDiB
aAUf5flXpTs+6IkWauUDiLZi4PlV7uiEexUq5FjirlL0U/6MjbudX4bK4WQ4uxDc
WaV07Kw2iJFOOHLDKT0en9JaX5jtJNc4ZnE9efFoQ5jfypPWtRw65G1rULEg6nvc
GDh+1ce+4foCkpLRC9c24xAwJONZG6x3UqrSS9qfAsb73nWRQrTfUcO3nhoN8VvL
kL9skn1+S06NyUN0KoEtyRBp+RcpXSsBWAo6qZmo/WqhB/gjzWrxVwn20+yJSm35
ZsMc6QECgYEA8GS+Mp9xfB2szWHz6YTOO1Uu4lHM1ccZMwS1G+dL0KO3uGAiPdvp
woVot6v6w88t7onXsLo5pgz7SYug0CpkF3K/MRd1Ar4lH7PK7IBQ6rFr9ppVxDbx
AEWRswUoPbKCr7W6HU8LbQHDavsDlEIwc6+DiwnL4BzlKjb7RpgQEz0CgYEA6sB5
uHvx3Y5FDcGk1n73leQSAcq14l3ZLNpjrs8msoREDil/j5WmuSN58/7PGMiMgHEi
1vLm3H796JmvGr9OBvspOjHyk07ui2/We/j9Hoxm1VWhyi8HkLNDj70HKalTTFMz
RHO4O+0xCva+h9mKZrRMVktXr2jjdFn/0MYIZ2ECgYAIIsC1IeRLWQ3CHbCNlKsO
IwHlMvOFwKk/qsceXKOaOhA7szU1dr3gkXdL0Aw6mEZrrkqYdpUA46uVf54/rU+Z
445I8QxKvXiwK/uQKX+TkdGflPWWIG3jnnch4ejMvb/ihnn4B/bRB6A/fKNQXzUY
lTYUfI5j1VaEKTwz1W2l2QKBgByFCcSp+jZqhGUpc3dDsZyaOr3Q/Mvlju7uEVI5
hIAHpaT60a6GBd1UPAqymEJwivFHzW3D0NxU6VAK68UaHMaoWNfjHY9b9YsnKS2i
kE3XzN56Ks+/avHfdYPO+UHMenw5V28nh+hv5pdoZrlmanQTz3pkaOC8o3WNQZEB
nh/BAoGBAMY5z2f1pmMhrvtPDSlEVjgjELbaInxFaxPLR4Pdyzn83gtIIU14+R8X
2LPs6PPwrNjWnIgrUSVXncIFL3pa45B+Mx1pYCpOAB1+nCZjIBQmpeo4Y0dwA/XH
85EthKPvoszm+OPbyI16OcePV5ocX7lupRYuAo0pek7bomhmHWHz
-----END RSA PRIVATE KEY-----`)
)
var (
ErrEmptyChain = errors.New("empty chain")
)
func setKeepAlive(conn net.Conn, d time.Duration) error {
c, ok := conn.(*net.TCPConn)
if !ok {
return errors.New("Not a TCP connection")
}
if err := c.SetKeepAlive(true); err != nil {
return err
}
if err := c.SetKeepAlivePeriod(d); err != nil {
return err
}
return nil
}
// Load the certificate from cert and key files, will use the default certificate if the provided info are invalid.
func LoadCertificate(certFile, keyFile string) (tls.Certificate, error) {
tlsCert, err := tls.LoadX509KeyPair(certFile, keyFile)
if err == nil {
return tlsCert, nil
}
glog.V(LWARNING).Infoln(err)
return tls.X509KeyPair(defaultRawCert, defaultRawKey)
}
// Replace the default certificate by your own
func SetDefaultCertificate(rawCert, rawKey []byte) {
defaultRawCert = rawCert
defaultRawKey = rawKey
}
func basicProxyAuth(proxyAuth string) (username, password string, ok bool) {
if proxyAuth == "" {
return
}
if !strings.HasPrefix(proxyAuth, "Basic ") {
return
}
c, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(proxyAuth, "Basic "))
if err != nil {
return
}
cs := string(c)
s := strings.IndexByte(cs, ':')
if s < 0 {
return
}
return cs[:s], cs[s+1:], true
}
This diff is collapsed.
// KCP feature is based on https://github.com/xtaci/kcptun
package gost
import (
"crypto/sha1"
"encoding/json"
"github.com/golang/glog"
"github.com/klauspost/compress/snappy"
"golang.org/x/crypto/pbkdf2"
"gopkg.in/xtaci/kcp-go.v2"
"gopkg.in/xtaci/smux.v1"
"net"
"os"
"time"
)
const (
DefaultKCPConfigFile = "kcp.json"
)
var (
SALT = "kcp-go"
)
type KCPConfig struct {
Key string `json:"key"`
Crypt string `json:"crypt"`
Mode string `json:"mode"`
MTU int `json:"mtu"`
SndWnd int `json:"sndwnd"`
RcvWnd int `json:"rcvwnd"`
DataShard int `json:"datashard"`
ParityShard int `json:"parityshard"`
DSCP int `json:"dscp"`
NoComp bool `json:"nocomp"`
AckNodelay bool `json:"acknodelay"`
NoDelay int `json:"nodelay"`
Interval int `json:"interval"`
Resend int `json:"resend"`
NoCongestion int `json:"nc"`
SockBuf int `json:"sockbuf"`
KeepAlive int `json:"keepalive"`
}
func ParseKCPConfig(configFile string) (*KCPConfig, error) {
if configFile == "" {
configFile = DefaultKCPConfigFile
}
file, err := os.Open(configFile)
if err != nil {
return nil, err
}
defer file.Close()
config := &KCPConfig{}
if err = json.NewDecoder(file).Decode(config); err != nil {
return nil, err
}
return config, nil
}
func (c *KCPConfig) Init() {
switch c.Mode {
case "normal":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 30, 2, 1
case "fast2":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 20, 2, 1
case "fast3":
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 1, 10, 2, 1
case "fast":
fallthrough
default:
c.NoDelay, c.Interval, c.Resend, c.NoCongestion = 0, 20, 2, 1
}
}
var (
DefaultKCPConfig = &KCPConfig{
Key: "it's a secrect",
Crypt: "aes",
Mode: "fast",
MTU: 1350,
SndWnd: 1024,
RcvWnd: 1024,
DataShard: 10,
ParityShard: 3,
DSCP: 0,
NoComp: false,
AckNodelay: false,
NoDelay: 0,
Interval: 40,
Resend: 0,
NoCongestion: 0,
SockBuf: 4194304,
KeepAlive: 10,
}
)
type KCPServer struct {
Base *ProxyServer
Config *KCPConfig
}
func NewKCPServer(base *ProxyServer, config *KCPConfig) *KCPServer {
return &KCPServer{Base: base, Config: config}
}
func (s *KCPServer) ListenAndServe() (err error) {
if s.Config == nil {
s.Config = DefaultKCPConfig
}
s.Config.Init()
ln, err := kcp.ListenWithOptions(s.Base.Node.Addr,
blockCrypt(s.Config.Key, s.Config.Crypt, SALT), s.Config.DataShard, s.Config.ParityShard)
if err != nil {
return err
}
if err = ln.SetDSCP(s.Config.DSCP); err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
}
if err = ln.SetReadBuffer(s.Config.SockBuf); err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
}
if err = ln.SetWriteBuffer(s.Config.SockBuf); err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
}
for {
conn, err := ln.AcceptKCP()
if err != nil {
glog.V(LWARNING).Infoln(err)
continue
}
conn.SetStreamMode(true)
conn.SetNoDelay(s.Config.NoDelay, s.Config.Interval, s.Config.Resend, s.Config.NoCongestion)
conn.SetMtu(s.Config.MTU)
conn.SetWindowSize(s.Config.SndWnd, s.Config.RcvWnd)
conn.SetACKNoDelay(s.Config.AckNodelay)
conn.SetKeepAlive(s.Config.KeepAlive)
go s.handleMux(conn)
}
}
func (s *KCPServer) handleMux(conn net.Conn) {
smuxConfig := smux.DefaultConfig()
smuxConfig.MaxReceiveBuffer = s.Config.SockBuf
glog.V(LINFO).Infof("[kcp] %s - %s", conn.RemoteAddr(), s.Base.Node.Addr)
if !s.Config.NoComp {
conn = newCompStreamConn(conn)
}
mux, err := smux.Server(conn, smuxConfig)
if err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
return
}
defer mux.Close()
glog.V(LINFO).Infof("[kcp] %s <-> %s", conn.RemoteAddr(), s.Base.Node.Addr)
defer glog.V(LINFO).Infof("[kcp] %s >-< %s", conn.RemoteAddr(), s.Base.Node.Addr)
for {
stream, err := mux.AcceptStream()
if err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
return
}
go s.Base.handleConn(NewKCPConn(conn, stream))
}
}
func blockCrypt(key, crypt, salt string) (block kcp.BlockCrypt) {
pass := pbkdf2.Key([]byte(key), []byte(salt), 4096, 32, sha1.New)
switch crypt {
case "tea":
block, _ = kcp.NewTEABlockCrypt(pass[:16])
case "xor":
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
case "none":
block, _ = kcp.NewNoneBlockCrypt(pass)
case "aes-128":
block, _ = kcp.NewAESBlockCrypt(pass[:16])
case "aes-192":
block, _ = kcp.NewAESBlockCrypt(pass[:24])
case "blowfish":
block, _ = kcp.NewBlowfishBlockCrypt(pass)
case "twofish":
block, _ = kcp.NewTwofishBlockCrypt(pass)
case "cast5":
block, _ = kcp.NewCast5BlockCrypt(pass[:16])
case "3des":
block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
case "xtea":
block, _ = kcp.NewXTEABlockCrypt(pass[:16])
case "salsa20":
block, _ = kcp.NewSalsa20BlockCrypt(pass)
case "aes":
fallthrough
default: // aes
block, _ = kcp.NewAESBlockCrypt(pass)
}
return
}
type KCPSession struct {
conn net.Conn
session *smux.Session
}
func DialKCP(addr string, config *KCPConfig) (*KCPSession, error) {
if config == nil {
config = DefaultKCPConfig
}
config.Init()
kcpconn, err := kcp.DialWithOptions(addr,
blockCrypt(config.Key, config.Crypt, SALT), config.DataShard, config.ParityShard)
if err != nil {
return nil, err
}
kcpconn.SetStreamMode(true)
kcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)
kcpconn.SetMtu(config.MTU)
kcpconn.SetACKNoDelay(config.AckNodelay)
kcpconn.SetKeepAlive(config.KeepAlive)
if err := kcpconn.SetDSCP(config.DSCP); err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
}
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
}
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
glog.V(LWARNING).Infoln("[kcp]", err)
}
// stream multiplex
smuxConfig := smux.DefaultConfig()
smuxConfig.MaxReceiveBuffer = config.SockBuf
var conn net.Conn = kcpconn
if !config.NoComp {
conn = newCompStreamConn(kcpconn)
}
session, err := smux.Client(conn, smuxConfig)
if err != nil {
conn.Close()
return nil, err
}
return &KCPSession{conn: conn, session: session}, nil
}
func (session *KCPSession) GetConn() (*KCPConn, error) {
stream, err := session.session.OpenStream()
if err != nil {
session.Close()
return nil, err
}
return NewKCPConn(session.conn, stream), nil
}
func (session *KCPSession) Close() error {
return session.session.Close()
}
func (session *KCPSession) IsClosed() bool {
return session.session.IsClosed()
}
func (session *KCPSession) NumStreams() int {
return session.session.NumStreams()
}
type KCPConn struct {
conn net.Conn
stream *smux.Stream
}
func NewKCPConn(conn net.Conn, stream *smux.Stream) *KCPConn {
return &KCPConn{conn: conn, stream: stream}
}
func (c *KCPConn) Read(b []byte) (n int, err error) {
return c.stream.Read(b)
}
func (c *KCPConn) Write(b []byte) (n int, err error) {
return c.stream.Write(b)
}
func (c *KCPConn) Close() error {
return c.stream.Close()
}
func (c *KCPConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *KCPConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *KCPConn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
func (c *KCPConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *KCPConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
type compStreamConn struct {
conn net.Conn
w *snappy.Writer
r *snappy.Reader
}
func newCompStreamConn(conn net.Conn) *compStreamConn {
c := new(compStreamConn)
c.conn = conn
c.w = snappy.NewBufferedWriter(conn)
c.r = snappy.NewReader(conn)
return c
}
func (c *compStreamConn) Read(b []byte) (n int, err error) {
return c.r.Read(b)
}
func (c *compStreamConn) Write(b []byte) (n int, err error) {
n, err = c.w.Write(b)
err = c.w.Flush()
return n, err
}
func (c *compStreamConn) Close() error {
return c.conn.Close()
}
func (c *compStreamConn) LocalAddr() net.Addr {
return c.conn.LocalAddr()
}
func (c *compStreamConn) RemoteAddr() net.Addr {
return c.conn.RemoteAddr()
}
func (c *compStreamConn) SetDeadline(t time.Time) error {
return c.conn.SetDeadline(t)
}
func (c *compStreamConn) SetReadDeadline(t time.Time) error {
return c.conn.SetReadDeadline(t)
}
func (c *compStreamConn) SetWriteDeadline(t time.Time) error {
return c.conn.SetWriteDeadline(t)
}
package gost
import (
"bufio"
"fmt"
"github.com/golang/glog"
"net"
"net/url"
"os"
"strconv"
"strings"
)
// Proxy node represent a proxy
type ProxyNode struct {
Addr string // [host]:port
Protocol string // protocol: http/socks5/ss
Transport string // transport: ws/wss/tls/http2/tcp/udp/rtcp/rudp
Remote string // remote address, used by tcp/udp port forwarding
Users []*url.Userinfo // authentication for proxy
values url.Values
serverName string
conn net.Conn
}
// The proxy node string pattern is [scheme://][user:pass@host]:port.
//
// Scheme can be devided into two parts by character '+', such as: http+tls.
func ParseProxyNode(s string) (node ProxyNode, err error) {
if !strings.Contains(s, "://") {
s = "gost://" + s
}
u, err := url.Parse(s)
if err != nil {
return
}
node = ProxyNode{
Addr: u.Host,
values: u.Query(),
serverName: u.Host,
}
if u.User != nil {
node.Users = append(node.Users, u.User)
}
users, er := parseUsers(node.Get("secrets"))
if users != nil {
node.Users = append(node.Users, users...)
}
if er != nil {
glog.V(LWARNING).Infoln("secrets:", er)
}
if strings.Contains(u.Host, ":") {
node.serverName, _, _ = net.SplitHostPort(u.Host)
if node.serverName == "" {
node.serverName = "localhost" // default server name
}
}
schemes := strings.Split(u.Scheme, "+")
if len(schemes) == 1 {
node.Protocol = schemes[0]
node.Transport = schemes[0]
}
if len(schemes) == 2 {
node.Protocol = schemes[0]
node.Transport = schemes[1]
}
switch node.Transport {
case "ws", "wss", "tls", "http2", "ssu", "quic", "kcp", "redirect":
case "https":
node.Protocol = "http"
node.Transport = "tls"
case "tcp", "udp": // started from v2.1, tcp and udp are for local port forwarding
node.Remote = strings.Trim(u.EscapedPath(), "/")
case "rtcp", "rudp": // started from v2.1, rtcp and rudp are for remote port forwarding
node.Remote = strings.Trim(u.EscapedPath(), "/")
default:
node.Transport = ""
}
switch node.Protocol {
case "http", "http2", "socks", "socks5", "ss":
default:
node.Protocol = ""
}
return
}
func parseUsers(authFile string) (users []*url.Userinfo, err error) {
if authFile == "" {
return
}
file, err := os.Open(authFile)
if err != nil {
return
}
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
s := strings.SplitN(line, " ", 2)
if len(s) == 1 {
users = append(users, url.User(strings.TrimSpace(s[0])))
} else if len(s) == 2 {
users = append(users, url.UserPassword(strings.TrimSpace(s[0]), strings.TrimSpace(s[1])))
}
}
err = scanner.Err()
return
}
// Get get node parameter by key
func (node *ProxyNode) Get(key string) string {
return node.values.Get(key)
}
func (node *ProxyNode) getBool(key string) bool {
s := node.Get(key)
if b, _ := strconv.ParseBool(s); b {
return b
}
n, _ := strconv.Atoi(s)
return n > 0
}
func (node *ProxyNode) Set(key, value string) {
node.values.Set(key, value)
}
func (node *ProxyNode) insecureSkipVerify() bool {
return !node.getBool("secure")
}
func (node *ProxyNode) certFile() string {
if cert := node.Get("cert"); cert != "" {
return cert
}
return DefaultCertFile
}
func (node *ProxyNode) keyFile() string {
if key := node.Get("key"); key != "" {
return key
}
return DefaultKeyFile
}
func (node ProxyNode) String() string {
return fmt.Sprintf("transport: %s, protocol: %s, addr: %s", node.Transport, node.Protocol, node.Addr)
}
package gost
import (
"bufio"
"crypto/tls"
"github.com/golang/glog"
"github.com/lucas-clemente/quic-go/h2quic"
"io"
"net/http"
"net/http/httputil"
)
type QuicServer struct {
Base *ProxyServer
Handler http.Handler
TLSConfig *tls.Config
}
func NewQuicServer(base *ProxyServer) *QuicServer {
return &QuicServer{Base: base}
}
func (s *QuicServer) ListenAndServeTLS(config *tls.Config) error {
server := &h2quic.Server{
Server: &http.Server{
Addr: s.Base.Node.Addr,
Handler: s.Handler,
TLSConfig: config,
},
}
if server.Handler == nil {
server.Handler = http.HandlerFunc(s.HandleRequest)
}
return server.ListenAndServe()
}
func (s *QuicServer) HandleRequest(w http.ResponseWriter, req *http.Request) {
target := req.Host
glog.V(LINFO).Infof("[quic] %s %s - %s %s", req.Method, req.RemoteAddr, target, req.Proto)
if glog.V(LDEBUG) {
dump, _ := httputil.DumpRequest(req, false)
glog.Infoln(string(dump))
}
c, err := s.Base.Chain.Dial(target)
if err != nil {
glog.V(LWARNING).Infof("[quic] %s -> %s : %s", req.RemoteAddr, target, err)
w.WriteHeader(http.StatusServiceUnavailable)
return
}
defer c.Close()
glog.V(LINFO).Infof("[quic] %s <-> %s", req.RemoteAddr, target)
req.Header.Set("Connection", "Keep-Alive")
if err = req.Write(c); err != nil {
glog.V(LWARNING).Infof("[quic] %s -> %s : %s", req.RemoteAddr, target, err)
return
}
resp, err := http.ReadResponse(bufio.NewReader(c), req)
if err != nil {
glog.V(LWARNING).Infoln(err)
return
}
defer resp.Body.Close()
for k, v := range resp.Header {
for _, vv := range v {
w.Header().Add(k, vv)
}
}
w.WriteHeader(resp.StatusCode)
if _, err := io.Copy(flushWriter{w}, resp.Body); err != nil {
glog.V(LWARNING).Infof("[quic] %s <- %s : %s", req.RemoteAddr, target, err)
}
glog.V(LINFO).Infof("[quic] %s >-< %s", req.RemoteAddr, target)
}
// +build !windows
package gost
import (
"errors"
"fmt"
"github.com/golang/glog"
"net"
"syscall"
)
const (
SO_ORIGINAL_DST = 80
)
type RedsocksTCPServer struct {
Base *ProxyServer
}
func NewRedsocksTCPServer(base *ProxyServer) *RedsocksTCPServer {
return &RedsocksTCPServer{
Base: base,
}
}
func (s *RedsocksTCPServer) ListenAndServe() error {
laddr, err := net.ResolveTCPAddr("tcp", s.Base.Node.Addr)
if err != nil {
return err
}
ln, err := net.ListenTCP("tcp", laddr)
if err != nil {
return err
}
defer ln.Close()
for {
conn, err := ln.AcceptTCP()
if err != nil {
glog.V(LWARNING).Infoln(err)
continue
}
go s.handleRedirectTCP(conn)
}
}
func (s *RedsocksTCPServer) handleRedirectTCP(conn *net.TCPConn) {
srcAddr := conn.RemoteAddr()
dstAddr, conn, err := getOriginalDstAddr(conn)
if err != nil {
glog.V(LWARNING).Infof("[red-tcp] %s -> %s : %s", srcAddr, dstAddr, err)
return
}
defer conn.Close()
glog.V(LINFO).Infof("[red-tcp] %s -> %s", srcAddr, dstAddr)
cc, err := s.Base.Chain.Dial(dstAddr.String())
if err != nil {
glog.V(LWARNING).Infof("[red-tcp] %s -> %s : %s", srcAddr, dstAddr, err)
return
}
defer cc.Close()
glog.V(LINFO).Infof("[red-tcp] %s <-> %s", srcAddr, dstAddr)
s.Base.transport(conn, cc)
glog.V(LINFO).Infof("[red-tcp] %s >-< %s", srcAddr, dstAddr)
}
func getOriginalDstAddr(conn *net.TCPConn) (addr net.Addr, c *net.TCPConn, err error) {
defer conn.Close()
fc, err := conn.File()
if err != nil {
return
}
defer fc.Close()
mreq, err := syscall.GetsockoptIPv6Mreq(int(fc.Fd()), syscall.IPPROTO_IP, SO_ORIGINAL_DST)
if err != nil {
return
}
// only ipv4 support
ip := net.IPv4(mreq.Multiaddr[4], mreq.Multiaddr[5], mreq.Multiaddr[6], mreq.Multiaddr[7])
port := uint16(mreq.Multiaddr[2])<<8 + uint16(mreq.Multiaddr[3])
addr, err = net.ResolveTCPAddr("tcp4", fmt.Sprintf("%s:%d", ip.String(), port))
if err != nil {
return
}
cc, err := net.FileConn(fc)
if err != nil {
return
}
c, ok := cc.(*net.TCPConn)
if !ok {
err = errors.New("not a TCP connection")
}
return
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
del old.txt
go test -bench=. >>old.txt && go test -bench=. >>old.txt && go test -bench=. >>old.txt && benchstat -delta-test=ttest old.txt new.txt
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment