Compare commits

...

5 Commits

Author SHA1 Message Date
V2Ray
28f87f2fae Update installation instruction 2015-09-29 09:16:10 +02:00
V2Ray
544b99b1a6 dispatch udp response 2015-09-28 21:32:07 +02:00
V2Ray
05b83508f8 Implementation of VMess UDP message 2015-09-28 17:13:50 +02:00
V2Ray
259d772f73 UDP message format 2015-09-28 15:10:42 +02:00
V2Ray
ba3f6108b8 assign port token for a udp packet 2015-09-28 14:57:43 +02:00
6 changed files with 299 additions and 8 deletions

View File

@@ -12,10 +12,11 @@ func NewTCPPacket(dest Destination) *TCPPacket {
}
}
func NewUDPPacket(dest Destination, data []byte) *UDPPacket {
func NewUDPPacket(dest Destination, data []byte, token uint16) *UDPPacket {
return &UDPPacket{
basePacket: basePacket{destination: dest},
data: data,
token: token,
}
}
@@ -41,7 +42,12 @@ func (packet *TCPPacket) MoreChunks() bool {
type UDPPacket struct {
basePacket
data []byte
data []byte
token uint16
}
func (packet *UDPPacket) Token() uint16 {
return packet.token
}
func (packet *UDPPacket) Chunk() []byte {

View File

@@ -1,8 +1,12 @@
package socks
import (
"math"
"math/rand"
"net"
"sync"
"github.com/v2ray/v2ray-core/common/collect"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy/socks/protocol"
@@ -12,6 +16,63 @@ const (
bufferSize = 2 * 1024
)
type portMap struct {
access sync.Mutex
data map[uint16]*net.UDPAddr
removedPorts *collect.TimedQueue
}
func newPortMap() *portMap {
m := &portMap{
access: sync.Mutex{},
data: make(map[uint16]*net.UDPAddr),
removedPorts: collect.NewTimedQueue(1),
}
go m.removePorts(m.removedPorts.RemovedEntries())
return m
}
func (m *portMap) assignAddressToken(addr *net.UDPAddr) uint16 {
for {
token := uint16(rand.Intn(math.MaxUint16))
if _, used := m.data[token]; !used {
m.access.Lock()
if _, used = m.data[token]; !used {
m.data[token] = addr
m.access.Unlock()
return token
}
m.access.Unlock()
}
}
}
func (m *portMap) removePorts(removedPorts <-chan interface{}) {
for {
rawToken := <-removedPorts
m.access.Lock()
delete(m.data, rawToken.(uint16))
m.access.Unlock()
}
}
func (m *portMap) popPort(token uint16) *net.UDPAddr {
m.access.Lock()
defer m.access.Unlock()
addr, exists := m.data[token]
if !exists {
return nil
}
delete(m.data, token)
return addr
}
var (
ports = newPortMap()
udpConn *net.UDPConn
)
func (server *SocksServer) ListenUDP(port uint16) error {
addr := &net.UDPAddr{
IP: net.IP{0, 0, 0, 0},
@@ -25,13 +86,14 @@ func (server *SocksServer) ListenUDP(port uint16) error {
}
go server.AcceptPackets(conn)
udpConn = conn
return nil
}
func (server *SocksServer) AcceptPackets(conn *net.UDPConn) error {
for {
buffer := make([]byte, 0, bufferSize)
nBytes, _, err := conn.ReadFromUDP(buffer)
nBytes, addr, err := conn.ReadFromUDP(buffer)
if err != nil {
log.Error("Socks failed to read UDP packets: %v", err)
return err
@@ -46,7 +108,20 @@ func (server *SocksServer) AcceptPackets(conn *net.UDPConn) error {
continue
}
udpPacket := v2net.NewUDPPacket(request.Destination(), request.Data)
token := ports.assignAddressToken(addr)
udpPacket := v2net.NewUDPPacket(request.Destination(), request.Data, token)
server.vPoint.DispatchToOutbound(udpPacket)
}
}
func (server *SocksServer) Dispatch(packet v2net.Packet) {
if udpPacket, ok := packet.(*v2net.UDPPacket); ok {
token := udpPacket.Token()
addr := ports.popPort(token)
if udpConn != nil {
udpConn.WriteToUDP(udpPacket.Chunk(), addr)
}
}
// We don't expect TCP Packets here
}

138
proxy/vmess/protocol/udp.go Normal file
View File

@@ -0,0 +1,138 @@
package protocol
import (
"crypto/aes"
"crypto/cipher"
"encoding/binary"
"hash/fnv"
"time"
"github.com/v2ray/v2ray-core/common/errors"
"github.com/v2ray/v2ray-core/common/log"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
)
type VMessUDP struct {
user user.ID
version byte
token uint16
address v2net.Address
data []byte
}
func ReadVMessUDP(buffer []byte, userset user.UserSet) (*VMessUDP, error) {
userHash := buffer[:user.IDBytesLen]
userId, timeSec, valid := userset.GetUser(userHash)
if !valid {
return nil, errors.NewAuthenticationError(userHash)
}
buffer = buffer[user.IDBytesLen:]
aesCipher, err := aes.NewCipher(userId.CmdKey())
if err != nil {
return nil, err
}
aesStream := cipher.NewCFBDecrypter(aesCipher, user.Int64Hash(timeSec))
aesStream.XORKeyStream(buffer, buffer)
fnvHash := binary.BigEndian.Uint32(buffer[:4])
fnv1a := fnv.New32a()
fnv1a.Write(buffer[4:])
fnvHashActual := fnv1a.Sum32()
if fnvHash != fnvHashActual {
log.Warning("Unexpected fhv hash %d, should be %d", fnvHashActual, fnvHash)
return nil, errors.NewCorruptedPacketError()
}
buffer = buffer[4:]
vmess := &VMessUDP{
user: *userId,
version: buffer[0],
token: binary.BigEndian.Uint16(buffer[1:3]),
}
// buffer[3] is reserved
port := binary.BigEndian.Uint16(buffer[4:6])
addrType := buffer[6]
var address v2net.Address
switch addrType {
case addrTypeIPv4:
address = v2net.IPAddress(buffer[7:11], port)
buffer = buffer[11:]
case addrTypeIPv6:
address = v2net.IPAddress(buffer[7:23], port)
buffer = buffer[23:]
case addrTypeDomain:
domainLength := buffer[7]
domain := string(buffer[8 : 8+domainLength])
address = v2net.DomainAddress(domain, port)
buffer = buffer[8+domainLength:]
default:
log.Warning("Unexpected address type %d", addrType)
return nil, errors.NewCorruptedPacketError()
}
vmess.address = address
vmess.data = buffer
return vmess, nil
}
func (vmess *VMessUDP) ToBytes(idHash user.CounterHash, randomRangeInt64 user.RandomInt64InRange, buffer []byte) []byte {
if buffer == nil {
buffer = make([]byte, 0, 2*1024)
}
counter := randomRangeInt64(time.Now().UTC().Unix(), 30)
hash := idHash.Hash(vmess.user.Bytes[:], counter)
buffer = append(buffer, hash...)
encryptBegin := 16
// Placeholder for fnv1a hash
buffer = append(buffer, byte(0), byte(0), byte(0), byte(0))
fnvHash := 16
fnvHashBegin := 20
buffer = append(buffer, vmess.version)
buffer = append(buffer, byte(vmess.token>>8), byte(vmess.token))
buffer = append(buffer, byte(0x00))
buffer = append(buffer, vmess.address.PortBytes()...)
switch {
case vmess.address.IsIPv4():
buffer = append(buffer, addrTypeIPv4)
buffer = append(buffer, vmess.address.IP()...)
case vmess.address.IsIPv6():
buffer = append(buffer, addrTypeIPv6)
buffer = append(buffer, vmess.address.IP()...)
case vmess.address.IsDomain():
buffer = append(buffer, addrTypeDomain)
buffer = append(buffer, byte(len(vmess.address.Domain())))
buffer = append(buffer, []byte(vmess.address.Domain())...)
}
buffer = append(buffer, vmess.data...)
fnv1a := fnv.New32a()
fnv1a.Write(buffer[fnvHashBegin:])
fnvHashValue := fnv1a.Sum32()
buffer[fnvHash] = byte(fnvHashValue >> 24)
buffer[fnvHash+1] = byte(fnvHashValue >> 16)
buffer[fnvHash+2] = byte(fnvHashValue >> 8)
buffer[fnvHash+3] = byte(fnvHashValue)
aesCipher, err := aes.NewCipher(vmess.user.CmdKey())
if err != nil {
log.Error("VMess failed to create AES cipher: %v", err)
return nil
}
aesStream := cipher.NewCFBEncrypter(aesCipher, user.Int64Hash(counter))
aesStream.XORKeyStream(buffer[encryptBegin:], buffer[encryptBegin:])
return buffer
}

View File

@@ -0,0 +1,43 @@
package protocol
import (
"testing"
v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/proxy/vmess/protocol/user"
"github.com/v2ray/v2ray-core/testing/mocks"
"github.com/v2ray/v2ray-core/testing/unit"
)
func TestVMessUDPReadWrite(t *testing.T) {
assert := unit.Assert(t)
userId, err := user.NewID("2b2966ac-16aa-4fbf-8d81-c5f172a3da51")
assert.Error(err).IsNil()
userSet := mocks.MockUserSet{[]user.ID{}, make(map[string]int), make(map[string]int64)}
userSet.AddUser(user.User{userId})
message := &VMessUDP{
user: userId,
version: byte(0x01),
token: 1234,
address: v2net.DomainAddress("v2ray.com", 8372),
data: []byte("An UDP message."),
}
mockTime := int64(1823730)
buffer := message.ToBytes(user.NewTimeHash(user.HMACHash{}), func(base int64, delta int) int64 { return mockTime }, nil)
userSet.UserHashes[string(buffer[:16])] = 0
userSet.Timestamps[string(buffer[:16])] = mockTime
messageRestored, err := ReadVMessUDP(buffer, &userSet)
assert.Error(err).IsNil()
assert.String(messageRestored.user.String).Equals(message.user.String)
assert.Byte(messageRestored.version).Equals(message.version)
assert.Uint16(messageRestored.token).Equals(message.token)
assert.String(messageRestored.address.String()).Equals(message.address.String())
assert.Bytes(messageRestored.data).Equals(message.data)
}

View File

@@ -1,8 +1,12 @@
# V2Ray 安装方式
目前 V2Ray 还在早期测试阶段,暂不提供预编译的运行文件。请使用下面的方式下载源文件并编译。
## 预编译程序
发布于 [Release](https://github.com/v2ray/v2ray-core/releases) 中,每周更新,[更新周期见此](https://github.com/V2Ray/v2ray-core/blob/master/spec/roadmap.md)。
## 编译源文件
大概流程,请根据实际情况修改
1. 安装 Git sudo apt-get install git -y
2. 安装 golang
1. curl -o go_latest.tar.gz https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz
@@ -12,7 +16,7 @@
3. go get github.com/v2ray/v2ray-core
4. go build github.com/v2ray/v2ray-core/release/server
### archlinux 编译源文件
### Arch Linux
1. 安装 Git sudo pacman -S git
2. 安装 golangsudo pacman -S go
1. export GOPATH=$HOME/work

View File

@@ -2,7 +2,8 @@
## 摘要
* 版本1
## 数据请求
## TCP
### 数据请求
认证部分:
* 16 字节:基于时间的 hash(用户 [ID](https://github.com/V2Ray/v2ray-core/blob/master/spec/id.md)),见下文
@@ -35,13 +36,37 @@
数据部分使用 AES-128-CFB 加密Key 和 IV 在请求数据中
## 数据应答
### 数据应答
数据部分
* 4 字节:认证信息 V
* N 字节:应答数据
其中数据部分使用 AES-128-CFB 加密IV 为 md5(请求数据 IV)Key 为 md5(请求数据 Key)
## UDP
UDP 数据包为对称设计,即请求和响应的格式一样
* 16 字节:基于时间的 hash(用户 [ID](https://github.com/V2Ray/v2ray-core/blob/master/spec/id.md)),见下文
* 4 字节:余下所有内容的 FNV1a hash
* 1 字节:版本号,目前为 0x1
* 2 字节Token用于区分数据包
* 1 字节:保留,暂为 0x00
* 2 字节:目标端口
* 1 字节:目标类型
* 0x01IPv4
* 0x02域名
* 0x03IPv6
* 目标地址:
* 4 字节IPv4
* 1 字节长度 + 域名
* 16 字节IPv6
* N 字节:请求数据
其中除了 hash 之外的部分经过 AES-128-CFB 加密:
* Keymd5(用户 ID + '22f01806-5ef0-4e88-95ab-b57f1c7a4a40')
* IVmd5(X + X + X + X)X = []byte(UserHash 生成的时间) (8 字节, Big Endian)
## 基于时间的用户 ID Hash
* H = MD5