Signed-off-by: arraykeys@gmail.com <arraykeys@gmail.com>
This commit is contained in:
@ -1,7 +1,8 @@
|
|||||||
proxy更新日志:
|
proxy更新日志:
|
||||||
v3.1
|
v3.1
|
||||||
1.优化了内网穿透功能,client只需要启动一个即可,server端启动的时候可以指定client端要暴露的端口。
|
1.优化了内网穿透功能,bridge,client和server只需要启动一个即可.
|
||||||
2.
|
server端启动的时候可以指定client端要暴露的一个或者多个端口。
|
||||||
|
2.此次更新,tserver模式部分参数不兼容3.0。
|
||||||
|
|
||||||
v3.0
|
v3.0
|
||||||
1.此次更新不兼容2.x版本,重构了全部代码,架构更合理,利于功能模块的增加与维护。
|
1.此次更新不兼容2.x版本,重构了全部代码,架构更合理,利于功能模块的增加与维护。
|
||||||
|
|||||||
@ -22,7 +22,12 @@ Proxy是golang实现的高性能http,https,websocket,tcp,udp代理服务器,支
|
|||||||
- 以前只能在局域网玩的,现在可以在任何地方玩.
|
- 以前只能在局域网玩的,现在可以在任何地方玩.
|
||||||
- 替代圣剑内网通,显IP内网通,花生壳之类的工具.
|
- 替代圣剑内网通,显IP内网通,花生壳之类的工具.
|
||||||
- ...
|
- ...
|
||||||
|
|
||||||
|
### 手册目录
|
||||||
|
本页是最新v3.1手册,其他手册请点击下面链接查看.
|
||||||
|
- [v3.0手册](https://github.com/snail007/goproxy/tree/v3.0)
|
||||||
|
- [v2.x手册](https://github.com/snail007/goproxy/tree/v2.2)
|
||||||
|
|
||||||
### Fast Start
|
### Fast Start
|
||||||
提示:所有操作需要root权限.
|
提示:所有操作需要root权限.
|
||||||
**0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.**
|
**0.如果你的VPS是linux64位的系统,那么只需要执行下面一句,就可以完成自动安装和配置.**
|
||||||
@ -45,7 +50,7 @@ wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_li
|
|||||||
下载地址:https://github.com/snail007/goproxy/releases
|
下载地址:https://github.com/snail007/goproxy/releases
|
||||||
```shell
|
```shell
|
||||||
cd /root/proxy/
|
cd /root/proxy/
|
||||||
wget https://github.com/snail007/goproxy/releases/download/v2.0/proxy-linux-amd64.tar.gz
|
wget https://github.com/snail007/goproxy/releases/download/v3.1/proxy-linux-amd64.tar.gz
|
||||||
```
|
```
|
||||||
**3.下载自动安装脚本**
|
**3.下载自动安装脚本**
|
||||||
```shell
|
```shell
|
||||||
|
|||||||
@ -78,8 +78,9 @@ func initConfig() (err error) {
|
|||||||
tunnelServerArgs.Timeout = tunnelServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
|
tunnelServerArgs.Timeout = tunnelServer.Flag("timeout", "tcp timeout with milliseconds").Short('t').Default("2000").Int()
|
||||||
tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool()
|
tunnelServerArgs.IsUDP = tunnelServer.Flag("udp", "proxy on udp tunnel server mode").Default("false").Bool()
|
||||||
tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String()
|
tunnelServerArgs.Key = tunnelServer.Flag("k", "client key").Default("default").String()
|
||||||
tunnelServerArgs.Remote = tunnelServer.Flag("remote", "client's network host:port").Short('r').Default("").String()
|
//tunnelServerArgs.Remote = tunnelServer.Flag("remote", "client's network host:port").Short('R').Default("").String()
|
||||||
tunnelServerArgs.Local = tunnelServer.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
|
//tunnelServerArgs.Local = tunnelServer.Flag("local", "local ip:port to listen").Short('p').Default(":33080").String()
|
||||||
|
tunnelServerArgs.Route = tunnelServer.Flag("route", "local route to client's network, such as :localip:localport@clienthost:clientport").Short('r').Default("").Strings()
|
||||||
|
|
||||||
//########tunnel-client#########
|
//########tunnel-client#########
|
||||||
tunnelClient := app.Command("tclient", "proxy on tunnel client mode")
|
tunnelClient := app.Command("tclient", "proxy on tunnel client mode")
|
||||||
@ -104,6 +105,7 @@ func initConfig() (err error) {
|
|||||||
tunnelBridgeArgs.Args = args
|
tunnelBridgeArgs.Args = args
|
||||||
tunnelClientArgs.Args = args
|
tunnelClientArgs.Args = args
|
||||||
tunnelServerArgs.Args = args
|
tunnelServerArgs.Args = args
|
||||||
|
*tunnelServerArgs.Route = []string{}
|
||||||
|
|
||||||
poster()
|
poster()
|
||||||
//regist services and run service
|
//regist services and run service
|
||||||
@ -111,9 +113,10 @@ func initConfig() (err error) {
|
|||||||
services.Regist("http", services.NewHTTP(), httpArgs)
|
services.Regist("http", services.NewHTTP(), httpArgs)
|
||||||
services.Regist("tcp", services.NewTCP(), tcpArgs)
|
services.Regist("tcp", services.NewTCP(), tcpArgs)
|
||||||
services.Regist("udp", services.NewUDP(), udpArgs)
|
services.Regist("udp", services.NewUDP(), udpArgs)
|
||||||
services.Regist("tserver", services.NewTunnelServer(), tunnelServerArgs)
|
services.Regist("tserver", services.NewTunnelServerManager(), tunnelServerArgs)
|
||||||
services.Regist("tclient", services.NewTunnelClient(), tunnelClientArgs)
|
services.Regist("tclient", services.NewTunnelClient(), tunnelClientArgs)
|
||||||
services.Regist("tbridge", services.NewTunnelBridge(), tunnelBridgeArgs)
|
services.Regist("tbridge", services.NewTunnelBridge(), tunnelBridgeArgs)
|
||||||
|
|
||||||
service, err = services.Run(serviceName)
|
service, err = services.Run(serviceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("run service [%s] fail, ERR:%s", service, err)
|
log.Fatalf("run service [%s] fail, ERR:%s", service, err)
|
||||||
|
|||||||
@ -6,7 +6,7 @@ fi
|
|||||||
mkdir /tmp/proxy
|
mkdir /tmp/proxy
|
||||||
cd /tmp/proxy
|
cd /tmp/proxy
|
||||||
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
|
wget https://github.com/reddec/monexec/releases/download/v0.1.1/monexec_0.1.1_linux_amd64.tar.gz
|
||||||
wget https://github.com/snail007/goproxy/releases/download/v3.0/proxy-linux-amd64.tar.gz
|
wget https://github.com/snail007/goproxy/releases/download/v3.1/proxy-linux-amd64.tar.gz
|
||||||
|
|
||||||
# install monexec
|
# install monexec
|
||||||
tar zxvf monexec_0.1.1_linux_amd64.tar.gz
|
tar zxvf monexec_0.1.1_linux_amd64.tar.gz
|
||||||
|
|||||||
@ -25,6 +25,7 @@ type TunnelServerArgs struct {
|
|||||||
Key *string
|
Key *string
|
||||||
Remote *string
|
Remote *string
|
||||||
Timeout *int
|
Timeout *int
|
||||||
|
Route *[]string
|
||||||
}
|
}
|
||||||
type TunnelClientArgs struct {
|
type TunnelClientArgs struct {
|
||||||
Args
|
Args
|
||||||
|
|||||||
@ -25,7 +25,7 @@ func Regist(name string, s Service, args interface{}) {
|
|||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func Run(name string) (service *ServiceItem, err error) {
|
func Run(name string, args ...interface{}) (service *ServiceItem, err error) {
|
||||||
service, ok := servicesMap[name]
|
service, ok := servicesMap[name]
|
||||||
if ok {
|
if ok {
|
||||||
go func() {
|
go func() {
|
||||||
@ -35,7 +35,11 @@ func Run(name string) (service *ServiceItem, err error) {
|
|||||||
log.Fatalf("%s servcie crashed, ERR: %s\ntrace:%s", name, err, string(debug.Stack()))
|
log.Fatalf("%s servcie crashed, ERR: %s\ntrace:%s", name, err, string(debug.Stack()))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
err := service.S.Start(service.Args)
|
if len(args) == 1 {
|
||||||
|
err = service.S.Start(args[0])
|
||||||
|
} else {
|
||||||
|
err = service.S.Start(service.Args)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("%s servcie fail, ERR: %s", name, err)
|
log.Fatalf("%s servcie fail, ERR: %s", name, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,44 @@ type TunnelServer struct {
|
|||||||
sc utils.ServerChannel
|
sc utils.ServerChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TunnelServerManager struct {
|
||||||
|
cfg TunnelServerArgs
|
||||||
|
udpChn chan UDPItem
|
||||||
|
sc utils.ServerChannel
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTunnelServerManager() Service {
|
||||||
|
return &TunnelServerManager{
|
||||||
|
cfg: TunnelServerArgs{},
|
||||||
|
udpChn: make(chan UDPItem, 50000),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (s *TunnelServerManager) Start(args interface{}) (err error) {
|
||||||
|
s.cfg = args.(TunnelServerArgs)
|
||||||
|
if *s.cfg.Parent != "" {
|
||||||
|
log.Printf("use tls parent %s", *s.cfg.Parent)
|
||||||
|
} else {
|
||||||
|
log.Fatalf("parent required")
|
||||||
|
}
|
||||||
|
for _, info := range *s.cfg.Route {
|
||||||
|
_routeInfo := strings.Split(info, "@")
|
||||||
|
server := NewTunnelServer()
|
||||||
|
local := _routeInfo[0]
|
||||||
|
remote := _routeInfo[1]
|
||||||
|
server.Start(TunnelServerArgs{
|
||||||
|
Args: s.cfg.Args,
|
||||||
|
Local: &local,
|
||||||
|
IsUDP: s.cfg.IsUDP,
|
||||||
|
Remote: &remote,
|
||||||
|
Key: s.cfg.Key,
|
||||||
|
Timeout: s.cfg.Timeout,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
func (s *TunnelServerManager) Clean() {
|
||||||
|
|
||||||
|
}
|
||||||
func NewTunnelServer() Service {
|
func NewTunnelServer() Service {
|
||||||
return &TunnelServer{
|
return &TunnelServer{
|
||||||
cfg: TunnelServerArgs{},
|
cfg: TunnelServerArgs{},
|
||||||
@ -37,11 +75,6 @@ func (s *TunnelServer) InitService() {
|
|||||||
s.UDPConnDeamon()
|
s.UDPConnDeamon()
|
||||||
}
|
}
|
||||||
func (s *TunnelServer) Check() {
|
func (s *TunnelServer) Check() {
|
||||||
if *s.cfg.Parent != "" {
|
|
||||||
log.Printf("use tls parent %s", *s.cfg.Parent)
|
|
||||||
} else {
|
|
||||||
log.Fatalf("parent required")
|
|
||||||
}
|
|
||||||
if *s.cfg.Remote == "" {
|
if *s.cfg.Remote == "" {
|
||||||
log.Fatalf("remote required")
|
log.Fatalf("remote required")
|
||||||
}
|
}
|
||||||
@ -122,7 +155,7 @@ func (s *TunnelServer) GetOutConn(id string) (outConn net.Conn, ID string, err e
|
|||||||
}
|
}
|
||||||
keyBytes := []byte(*s.cfg.Key)
|
keyBytes := []byte(*s.cfg.Key)
|
||||||
keyLength := uint16(len(keyBytes))
|
keyLength := uint16(len(keyBytes))
|
||||||
ID = utils.NewUniqueID().String()
|
ID = utils.Uniqueid()
|
||||||
IDBytes := []byte(ID)
|
IDBytes := []byte(ID)
|
||||||
if id != "" {
|
if id != "" {
|
||||||
ID = id
|
ID = id
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -305,6 +306,11 @@ func ReadUDPPacket(_reader io.Reader) (srcAddr string, packet []byte, err error)
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
func Uniqueid() string {
|
||||||
|
var src = rand.NewSource(time.Now().UnixNano())
|
||||||
|
s := fmt.Sprintf("%d", src.Int63())
|
||||||
|
return s[len(s)-5:len(s)-1] + fmt.Sprintf("%d", uint64(time.Now().UnixNano()))[8:]
|
||||||
|
}
|
||||||
|
|
||||||
// type sockaddr struct {
|
// type sockaddr struct {
|
||||||
// family uint16
|
// family uint16
|
||||||
|
|||||||
264
utils/id.go
264
utils/id.go
@ -1,264 +0,0 @@
|
|||||||
// Package xid is a globally unique id generator suited for web scale
|
|
||||||
//
|
|
||||||
// Xid is using Mongo Object ID algorithm to generate globally unique ids:
|
|
||||||
// https://docs.mongodb.org/manual/reference/object-id/
|
|
||||||
//
|
|
||||||
// - 4-byte value representing the seconds since the Unix epoch,
|
|
||||||
// - 3-byte machine identifier,
|
|
||||||
// - 2-byte process id, and
|
|
||||||
// - 3-byte counter, starting with a random value.
|
|
||||||
//
|
|
||||||
// The binary representation of the id is compatible with Mongo 12 bytes Object IDs.
|
|
||||||
// The string representation is using base32 hex (w/o padding) for better space efficiency
|
|
||||||
// when stored in that form (20 bytes). The hex variant of base32 is used to retain the
|
|
||||||
// sortable property of the id.
|
|
||||||
//
|
|
||||||
// Xid doesn't use base64 because case sensitivity and the 2 non alphanum chars may be an
|
|
||||||
// issue when transported as a string between various systems. Base36 wasn't retained either
|
|
||||||
// because 1/ it's not standard 2/ the resulting size is not predictable (not bit aligned)
|
|
||||||
// and 3/ it would not remain sortable. To validate a base32 `xid`, expect a 20 chars long,
|
|
||||||
// all lowercase sequence of `a` to `v` letters and `0` to `9` numbers (`[0-9a-v]{20}`).
|
|
||||||
//
|
|
||||||
// UUID is 16 bytes (128 bits), snowflake is 8 bytes (64 bits), xid stands in between
|
|
||||||
// with 12 bytes with a more compact string representation ready for the web and no
|
|
||||||
// required configuration or central generation server.
|
|
||||||
//
|
|
||||||
// Features:
|
|
||||||
//
|
|
||||||
// - Size: 12 bytes (96 bits), smaller than UUID, larger than snowflake
|
|
||||||
// - Base32 hex encoded by default (16 bytes storage when transported as printable string)
|
|
||||||
// - Non configured, you don't need set a unique machine and/or data center id
|
|
||||||
// - K-ordered
|
|
||||||
// - Embedded time with 1 second precision
|
|
||||||
// - Unicity guaranted for 16,777,216 (24 bits) unique ids per second and per host/process
|
|
||||||
//
|
|
||||||
// Best used with xlog's RequestIDHandler (https://godoc.org/github.com/rs/xlog#RequestIDHandler).
|
|
||||||
//
|
|
||||||
// References:
|
|
||||||
//
|
|
||||||
// - http://www.slideshare.net/davegardnerisme/unique-id-generation-in-distributed-systems
|
|
||||||
// - https://en.wikipedia.org/wiki/Universally_unique_identifier
|
|
||||||
// - https://blog.twitter.com/2010/announcing-snowflake
|
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/md5"
|
|
||||||
"crypto/rand"
|
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/binary"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Code inspired from mgo/bson ObjectId
|
|
||||||
|
|
||||||
// ID represents a unique request id
|
|
||||||
type ID [rawLen]byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
encodedLen = 20 // string encoded len
|
|
||||||
decodedLen = 15 // len after base32 decoding with the padded data
|
|
||||||
rawLen = 12 // binary raw len
|
|
||||||
|
|
||||||
// encoding stores a custom version of the base32 encoding with lower case
|
|
||||||
// letters.
|
|
||||||
encoding = "0123456789abcdefghijklmnopqrstuv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ErrInvalidID is returned when trying to unmarshal an invalid ID
|
|
||||||
var ErrInvalidID = errors.New("xid: invalid ID")
|
|
||||||
|
|
||||||
// objectIDCounter is atomically incremented when generating a new ObjectId
|
|
||||||
// using NewObjectId() function. It's used as a counter part of an id.
|
|
||||||
// This id is initialized with a random value.
|
|
||||||
var objectIDCounter = randInt()
|
|
||||||
|
|
||||||
// machineId stores machine id generated once and used in subsequent calls
|
|
||||||
// to NewObjectId function.
|
|
||||||
var machineID = readMachineID()
|
|
||||||
|
|
||||||
// pid stores the current process id
|
|
||||||
var pid = os.Getpid()
|
|
||||||
|
|
||||||
// dec is the decoding map for base32 encoding
|
|
||||||
var dec [256]byte
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for i := 0; i < len(dec); i++ {
|
|
||||||
dec[i] = 0xFF
|
|
||||||
}
|
|
||||||
for i := 0; i < len(encoding); i++ {
|
|
||||||
dec[encoding[i]] = byte(i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// readMachineId generates machine id and puts it into the machineId global
|
|
||||||
// variable. If this function fails to get the hostname, it will cause
|
|
||||||
// a runtime error.
|
|
||||||
func readMachineID() []byte {
|
|
||||||
id := make([]byte, 3)
|
|
||||||
if hostname, err := os.Hostname(); err == nil {
|
|
||||||
hw := md5.New()
|
|
||||||
hw.Write([]byte(hostname))
|
|
||||||
copy(id, hw.Sum(nil))
|
|
||||||
} else {
|
|
||||||
// Fallback to rand number if machine id can't be gathered
|
|
||||||
if _, randErr := rand.Reader.Read(id); randErr != nil {
|
|
||||||
panic(fmt.Errorf("xid: cannot get hostname nor generate a random number: %v; %v", err, randErr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
// randInt generates a random uint32
|
|
||||||
func randInt() uint32 {
|
|
||||||
b := make([]byte, 3)
|
|
||||||
if _, err := rand.Reader.Read(b); err != nil {
|
|
||||||
panic(fmt.Errorf("xid: cannot generate random number: %v;", err))
|
|
||||||
}
|
|
||||||
return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewUniqueID generates a globaly unique ID
|
|
||||||
func NewUniqueID() ID {
|
|
||||||
var id ID
|
|
||||||
// Timestamp, 4 bytes, big endian
|
|
||||||
binary.BigEndian.PutUint32(id[:], uint32(time.Now().Unix()))
|
|
||||||
// Machine, first 3 bytes of md5(hostname)
|
|
||||||
id[4] = machineID[0]
|
|
||||||
id[5] = machineID[1]
|
|
||||||
id[6] = machineID[2]
|
|
||||||
// Pid, 2 bytes, specs don't specify endianness, but we use big endian.
|
|
||||||
id[7] = byte(pid >> 8)
|
|
||||||
id[8] = byte(pid)
|
|
||||||
// Increment, 3 bytes, big endian
|
|
||||||
i := atomic.AddUint32(&objectIDCounter, 1)
|
|
||||||
id[9] = byte(i >> 16)
|
|
||||||
id[10] = byte(i >> 8)
|
|
||||||
id[11] = byte(i)
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromString reads an ID from its string representation
|
|
||||||
func FromString(id string) (ID, error) {
|
|
||||||
i := &ID{}
|
|
||||||
err := i.UnmarshalText([]byte(id))
|
|
||||||
return *i, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a base32 hex lowercased with no padding representation of the id (char set is 0-9, a-v).
|
|
||||||
func (id ID) String() string {
|
|
||||||
text := make([]byte, encodedLen)
|
|
||||||
encode(text, id[:])
|
|
||||||
return string(text)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MarshalText implements encoding/text TextMarshaler interface
|
|
||||||
func (id ID) MarshalText() ([]byte, error) {
|
|
||||||
text := make([]byte, encodedLen)
|
|
||||||
encode(text, id[:])
|
|
||||||
return text, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// encode by unrolling the stdlib base32 algorithm + removing all safe checks
|
|
||||||
func encode(dst, id []byte) {
|
|
||||||
dst[0] = encoding[id[0]>>3]
|
|
||||||
dst[1] = encoding[(id[1]>>6)&0x1F|(id[0]<<2)&0x1F]
|
|
||||||
dst[2] = encoding[(id[1]>>1)&0x1F]
|
|
||||||
dst[3] = encoding[(id[2]>>4)&0x1F|(id[1]<<4)&0x1F]
|
|
||||||
dst[4] = encoding[id[3]>>7|(id[2]<<1)&0x1F]
|
|
||||||
dst[5] = encoding[(id[3]>>2)&0x1F]
|
|
||||||
dst[6] = encoding[id[4]>>5|(id[3]<<3)&0x1F]
|
|
||||||
dst[7] = encoding[id[4]&0x1F]
|
|
||||||
dst[8] = encoding[id[5]>>3]
|
|
||||||
dst[9] = encoding[(id[6]>>6)&0x1F|(id[5]<<2)&0x1F]
|
|
||||||
dst[10] = encoding[(id[6]>>1)&0x1F]
|
|
||||||
dst[11] = encoding[(id[7]>>4)&0x1F|(id[6]<<4)&0x1F]
|
|
||||||
dst[12] = encoding[id[8]>>7|(id[7]<<1)&0x1F]
|
|
||||||
dst[13] = encoding[(id[8]>>2)&0x1F]
|
|
||||||
dst[14] = encoding[(id[9]>>5)|(id[8]<<3)&0x1F]
|
|
||||||
dst[15] = encoding[id[9]&0x1F]
|
|
||||||
dst[16] = encoding[id[10]>>3]
|
|
||||||
dst[17] = encoding[(id[11]>>6)&0x1F|(id[10]<<2)&0x1F]
|
|
||||||
dst[18] = encoding[(id[11]>>1)&0x1F]
|
|
||||||
dst[19] = encoding[(id[11]<<4)&0x1F]
|
|
||||||
}
|
|
||||||
|
|
||||||
// UnmarshalText implements encoding/text TextUnmarshaler interface
|
|
||||||
func (id *ID) UnmarshalText(text []byte) error {
|
|
||||||
if len(text) != encodedLen {
|
|
||||||
return ErrInvalidID
|
|
||||||
}
|
|
||||||
for _, c := range text {
|
|
||||||
if dec[c] == 0xFF {
|
|
||||||
return ErrInvalidID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
decode(id, text)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// decode by unrolling the stdlib base32 algorithm + removing all safe checks
|
|
||||||
func decode(id *ID, src []byte) {
|
|
||||||
id[0] = dec[src[0]]<<3 | dec[src[1]]>>2
|
|
||||||
id[1] = dec[src[1]]<<6 | dec[src[2]]<<1 | dec[src[3]]>>4
|
|
||||||
id[2] = dec[src[3]]<<4 | dec[src[4]]>>1
|
|
||||||
id[3] = dec[src[4]]<<7 | dec[src[5]]<<2 | dec[src[6]]>>3
|
|
||||||
id[4] = dec[src[6]]<<5 | dec[src[7]]
|
|
||||||
id[5] = dec[src[8]]<<3 | dec[src[9]]>>2
|
|
||||||
id[6] = dec[src[9]]<<6 | dec[src[10]]<<1 | dec[src[11]]>>4
|
|
||||||
id[7] = dec[src[11]]<<4 | dec[src[12]]>>1
|
|
||||||
id[8] = dec[src[12]]<<7 | dec[src[13]]<<2 | dec[src[14]]>>3
|
|
||||||
id[9] = dec[src[14]]<<5 | dec[src[15]]
|
|
||||||
id[10] = dec[src[16]]<<3 | dec[src[17]]>>2
|
|
||||||
id[11] = dec[src[17]]<<6 | dec[src[18]]<<1 | dec[src[19]]>>4
|
|
||||||
}
|
|
||||||
|
|
||||||
// Time returns the timestamp part of the id.
|
|
||||||
// It's a runtime error to call this method with an invalid id.
|
|
||||||
func (id ID) Time() time.Time {
|
|
||||||
// First 4 bytes of ObjectId is 32-bit big-endian seconds from epoch.
|
|
||||||
secs := int64(binary.BigEndian.Uint32(id[0:4]))
|
|
||||||
return time.Unix(secs, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Machine returns the 3-byte machine id part of the id.
|
|
||||||
// It's a runtime error to call this method with an invalid id.
|
|
||||||
func (id ID) Machine() []byte {
|
|
||||||
return id[4:7]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pid returns the process id part of the id.
|
|
||||||
// It's a runtime error to call this method with an invalid id.
|
|
||||||
func (id ID) Pid() uint16 {
|
|
||||||
return binary.BigEndian.Uint16(id[7:9])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Counter returns the incrementing value part of the id.
|
|
||||||
// It's a runtime error to call this method with an invalid id.
|
|
||||||
func (id ID) Counter() int32 {
|
|
||||||
b := id[9:12]
|
|
||||||
// Counter is stored as big-endian 3-byte value
|
|
||||||
return int32(uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2]))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value implements the driver.Valuer interface.
|
|
||||||
func (id ID) Value() (driver.Value, error) {
|
|
||||||
b, err := id.MarshalText()
|
|
||||||
return string(b), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scan implements the sql.Scanner interface.
|
|
||||||
func (id *ID) Scan(value interface{}) (err error) {
|
|
||||||
switch val := value.(type) {
|
|
||||||
case string:
|
|
||||||
return id.UnmarshalText([]byte(val))
|
|
||||||
case []byte:
|
|
||||||
return id.UnmarshalText(val)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("xid: scanning unsupported type: %T", value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user