Commit 7a28cead authored by wei.xuan's avatar wei.xuan

v0.0.3

parent 90c6e60d
...@@ -2,4 +2,8 @@ ...@@ -2,4 +2,8 @@
echo-cli echo-cli
dist/config.yaml dist/config.yaml
dist dist
majora-cli majora-cli
\ No newline at end of file bin
nohup.out
release
./majora
\ No newline at end of file
export GO111MODULE=on
export GOPROXY="https://goproxy.cn,https://goproxy.io,direct"
LDFLAGS := -s -w
DATE=$(shell date +"%Y-%m-%d")
BUILDINFO := -X main.Version=v0.0.3 -X main.Date=$(DATE)
all:
env CGO_ENABLED=0 go build -trimpath -ldflags '-w -s $(BUILDINFO)' -o bin/majora
clean:
rm -fr majora-go
rm -fr majora
\ No newline at end of file
export PATH := $(GOPATH)/bin:$(PATH)
export GO111MODULE=on
LDFLAGS := -s -w
DATE=$(shell date +"%Y-%m-%d")
version=v0.0.3
BUILDINFO := -X main.Version=$(version) -X main.Date=$(DATE)
os-archs=darwin:amd64 darwin:arm64 freebsd:386 freebsd:amd64 linux:386 linux:amd64 linux:arm linux:arm64 windows:386 windows:amd64 linux:mips64 linux:mips64le
all: build
build: app
app:
@$(foreach n, $(os-archs),\
os=$(shell echo "$(n)" | cut -d : -f 1);\
arch=$(shell echo "$(n)" | cut -d : -f 2);\
gomips=$(shell echo "$(n)" | cut -d : -f 3);\
target_suffix=$${os}_$${arch};\
echo "Build $${os}-$${arch}...";\
env CGO_ENABLED=0 GOOS=$${os} GOARCH=$${arch} GOMIPS=$${gomips} go build -trimpath -ldflags '-w -s $(BUILDINFO)' -o ./release/majora_$${target_suffix};\
echo "Build $${os}-$${arch} done";\
)
@mv ./release/majora_windows_386 ./release/majora_windows_386.exe
@mv ./release/majora_windows_amd64 ./release/majora_windows_amd64.exe
\ No newline at end of file
# 本地编译 # 安装
> 从v0.0.3 版本开始,支持通过配置文件的方式启动
``` ## 下载地址
make build 打开 [](https://oss.virjar.com/majora/bin) 选择 最新的版本
``` ![](download.png)
![](download2.png)
# 运行 ## 解压
![img_1.png](start.png)
## 启动
``` ```
./majora-cli -natServer "127.0.0.1:5879" nohup bash start.sh >> std.log 2>&1 &
``` ```
成功的界面
![img_2.png](start2.png)
# 查看帮助 # 配置文件说明
> 配置文件 默认是 同级目录的 majora.ini 可按需自行修改
```
./majora-cli -h
Usage of ./majora-cli:
-account string
account (default "unknown")
-debugPort string
debugPort (default "127.0.0.1:6060")
-log int
log logLevel (default 1)
-natServer string
natServer (default "majora.virjar.com:5879")
-pprof
enable pprof
``` ```
; 绑定的nat server 地址
> 说明 ; 线上是 majora.virjar.com:5879 测试是 aoba.vip:5879
tunnel_addr = aoba.vip:5879
- -account 标识当前用户 默认是unknown ;一般情况下无需修改
- -debugPort 可忽略 用于性能分析 dns_server = 114.114.114.114:53
- -natServer 指定natserver 默认是 majora.virjar.com:5879 ;bind to local ip 针对多网卡模式
- -log 指定log level local_ip = 192.168.0.100
; 是否关闭自动更新
# 各平台二进制 disable_update = false
; 日志级别
放在dist 目录下 log_level = 1
; 服务端异常时的重连间隔
# 启动成功界面 reconn_interval = 5s
; 不指定时 会随机生成
![](img.png) client_id =
\ No newline at end of file
[extra]
account = superman
[redial]
; 重播的命令
command = /bin/bash
; 重播的执行脚本
exec_path = /root/ppp_auto_with_auth.sh
; 重播的周期
redial_duration = 5m
; 重播执行的时间
wait_time = 10s
```
\ No newline at end of file
#!/usr/bin/env bash
export GO111MODULE="on"
export GOPROXY="https://goproxy.cn,https://goproxy.io,direct"
export CGO_ENABLE=1
DATE=$(date "+%Y%m%d-%H%M%S")
mkdir "$1"
BINARY=majora
VERSION=$1
GOVER=latest
BUILDINFO="-X main.Version=$VERSION -X main.Date=$DATE"
EXTFLAG="--extldflags \"-static -fpic\""
OUTDIR=dist/$1
OUT=$OUTDIR/$BINARY-$1
TOKEN=Aau6PvRI4o0OTYrgpQHzxG7qEDkADx6CaUAJV2
TOKENONLINE=456734sdlasysdhf293r23r
echo "$OUTDIR"
echo "$OUT"
build_local() {
echo "build local..."
go build -a -trimpath -ldflags "-w -s $BUILDINFO" -o $BINARY
if [ $? -ne 0 ]; then
echo "build local fail"
exit 1
fi
echo "build success"
}
build_release() {
xgo -x -out "$OUT" -goproxy $GOPROXY -go $GOVER -ldflags="-s -w $EXTFLAG $BUILDINFO" /home/xuan/code/gcode/majora-go
}
upload() {
sudo chown -R xuan dist
cd "$OUTDIR"
for file in `ls | grep majora`; do
newfile=$(echo "${file}" | sed 's/-10.12//g;s/-4.0//g')
echo "uploading $newfile ..."
curl -F file=@"${file}" -F filename="${newfile}" -F token=$TOKEN http://81.70.224.147:10010/version
curl -F file=@"${file}" -F filename="${newfile}" -F token=$TOKENONLINE https://oss.virjar.com/majora/bin
done
touch latest.txt
echo "$VERSION" > latest.txt
curl -F file=@latest.txt -F token=$TOKEN http://81.70.224.147:10010/version
curl -F file=@latest.txt -F token=$TOKENONLINE https://oss.virjar.com/majora/bin
}
build_local
build_release
upload
\ No newline at end of file
...@@ -2,28 +2,32 @@ package main ...@@ -2,28 +2,32 @@ package main
import ( import (
"context" "context"
"encoding/json"
"flag" "flag"
"fmt"
"log" "log"
"math/rand" "math/rand"
"net" "net"
"net/http" "net/http"
_ "net/http/pprof" //nolint:gosec _ "net/http/pprof" //nolint:gosec
"os"
"runtime" "runtime"
"time" "time"
"virjar.com/majora-go/client" "virjar.com/majora-go/client"
"virjar.com/majora-go/common" "virjar.com/majora-go/common"
"virjar.com/majora-go/infra"
"virjar.com/majora-go/logger" "virjar.com/majora-go/logger"
"virjar.com/majora-go/updater" "virjar.com/majora-go/model"
) )
var ( var (
configure string
logLevel int logLevel int
pprof bool pprofPort int
natServer string natServer string
account string account string
dnsServer string dnsServer string
disableDNS bool
localAddr string localAddr string
disableUpdate bool disableUpdate bool
) )
...@@ -35,29 +39,28 @@ var ( ...@@ -35,29 +39,28 @@ var (
func init() { func init() {
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
flag.StringVar(&configure, "conf", "", "./majora -c path/to/your/majora.ini")
flag.IntVar(&logLevel, "log", 1, "log logLevel") flag.IntVar(&logLevel, "log", 1, "log logLevel")
flag.BoolVar(&pprof, "pprof", false, "enable pprof") flag.IntVar(&pprofPort, "pprof", 0, "enable pprof")
flag.StringVar(&natServer, "natServer", common.DefNatAddr, "natServer") flag.StringVar(&natServer, "natServer", common.DefNatAddr, "natServer")
flag.StringVar(&account, "account", "unknown", "account") flag.StringVar(&account, "account", "unknown", "account")
flag.StringVar(&dnsServer, "dnsServer", common.DNSServer, "custom dns server") flag.StringVar(&dnsServer, "dnsServer", common.DNSServer, "custom dns server")
flag.BoolVar(&disableDNS, "disableDns", false, "disable default dns server")
flag.StringVar(&localAddr, "localIp", "", "bind local ip") flag.StringVar(&localAddr, "localIp", "", "bind local ip")
flag.BoolVar(&disableUpdate, "disableUpdate", false, "disable self update") flag.BoolVar(&disableUpdate, "disableUpdate", false, "disable self update")
flag.Parse() flag.Parse()
} }
func initPprof() { func initial(cfg *model.Configure) {
if !pprof { logger.SetLogLevel(cfg.LogLevel)
return if cfg.PprofPort > 0 {
go func() {
log.Printf("enable pprof: %s", common.PprofAddr)
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", cfg.PprofPort), nil))
}()
} }
go func() {
log.Printf("enable pprof: %s", common.PprofAddr)
log.Fatal(http.ListenAndServe(common.PprofAddr, nil))
}()
// for android if len(cfg.DNSServer) > 0 {
if !disableDNS {
net.DefaultResolver = &net.Resolver{ net.DefaultResolver = &net.Resolver{
PreferGo: true, PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) { Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
...@@ -66,25 +69,45 @@ func initPprof() { ...@@ -66,25 +69,45 @@ func initPprof() {
} }
logger.Info().Msgf("use custom dns server %s", dnsServer) logger.Info().Msgf("use custom dns server %s", dnsServer)
} }
}
func update() { if !cfg.DisableUpdate {
if disableUpdate { if _, err := infra.Update("majora", Version); err != nil {
return logger.Error().Msgf("check update error %+s", err.Error())
}
} }
}
if _, err := updater.Update("majora", Version); err != nil { func parseFromCmd(cfg *model.Configure) {
logger.Error().Msgf("check update error %+s", err.Error()) cfg.TunnelAddr = natServer
} cfg.LogLevel = logLevel
cfg.DNSServer = dnsServer
cfg.LocalAddr = localAddr
cfg.DisableUpdate = disableUpdate
// 先兼容吧
cfg.Extra.Account = account
cfg.PprofPort = pprofPort
} }
func main() { func cli() {
initPprof() cfg := model.NewDefMajoraConf()
logger.SetLogLevel(logLevel) if len(configure) > 0 {
cfg = model.InitConf(configure)
} else {
parseFromCmd(cfg)
}
initial(cfg)
logger.Info().Msgf("current Version %s, build at %s", Version, Date) logger.Info().Msgf("current Version %s, build at %s", Version, Date)
update()
logger.Info().Msgf("hostinfo os:%s, arch:%s", runtime.GOOS, runtime.GOARCH) logger.Info().Msgf("hostinfo os:%s, arch:%s", runtime.GOOS, runtime.GOARCH)
client.NewClient(client.WithNatServerAddr(natServer), cfgInfo, _ := json.Marshal(cfg)
client.WithLocalIP(localAddr), logger.Info().Msgf("config info:%s", string(cfgInfo))
client.WithAccount(account)) client.NewClientWithConf(cfg)
}
//main start
func main() {
if len(os.Args) > 1 && os.Args[1] == "version" {
fmt.Println(Version)
os.Exit(0)
}
cli()
} }
...@@ -3,70 +3,92 @@ package client ...@@ -3,70 +3,92 @@ package client
import ( import (
"bufio" "bufio"
"errors" "errors"
"fmt"
"io" "io"
"net" "net"
"os"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
"github.com/google/uuid"
"virjar.com/majora-go/common" "virjar.com/majora-go/common"
"virjar.com/majora-go/logger" "virjar.com/majora-go/logger"
"virjar.com/majora-go/model"
"virjar.com/majora-go/protocol" "virjar.com/majora-go/protocol"
) )
func NewOptions() *Options {
return &Options{
NatHostPort: common.DefNatAddr,
ClientID: uuid.New().String(),
ExtraInfo: make(Extra),
}
}
type Client struct { type Client struct {
Options *Options config *model.Configure
localAddr net.Addr
natTunnel atomic.Value natTunnel atomic.Value
Codec protocol.ICodec Codec protocol.ICodec
connStore sync.Map connStore sync.Map
cleanup chan struct{}
} }
func NewClient(opts ...Option) { func NewClientWithConf(cfg *model.Configure) {
options := NewOptions() client := NewCli(cfg)
for _, opt := range opts {
opt(options)
}
client := &Client{Options: options, Codec: protocol.Codec}
client.StartUp() client.StartUp()
} }
func (client *Client) StartUp() { func NewCli(cfg *model.Configure) *Client {
if client.Options == nil { var localAddr net.Addr
client.Options = NewOptions() if len(cfg.LocalAddr) > 0 {
logger.Warn().Msgf("use default nat addr %s", common.DefNatServerHost) localAddr = &net.TCPAddr{
IP: net.ParseIP(cfg.LocalAddr),
Port: 0,
}
}
client := &Client{
config: cfg,
localAddr: localAddr,
natTunnel: atomic.Value{},
Codec: protocol.Codec,
connStore: sync.Map{},
cleanup: make(chan struct{}),
} }
return client
}
func (client *Client) StartUp() {
client.connect() client.connect()
client.register() client.register()
client.handleNatEvent()
if client.config.Redial.Invalid() {
client.Redial()
}
go func() {
client.handleNatEvent()
}()
// 退出旧的进程
for range client.cleanup {
os.Exit(0)
}
} }
func (client *Client) register() { func (client *Client) register() {
packet := protocol.TypeRegister.CreatePacket() packet := protocol.TypeRegister.CreatePacket()
packet.Extra = client.Options.ClientID packet.Extra = client.config.ClientID
packet.Data = protocol.EncodeExtra(client.Options.ExtraInfo) extraMap := make(map[string]string, 1)
extraMap[common.ExtrakeyUser] = client.config.Extra.Account
packet.Data = protocol.EncodeExtra(extraMap)
if err := client.WriteAndFlush(packet); err != nil { if err := client.WriteAndFlush(packet); err != nil {
logger.Error().Msgf("register to nat server with error %s", err.Error()) logger.Error().Msgf("register to nat server with error %s", err.Error())
} else { } else {
logger.Info().Msg("register to nat server success") logger.Info().Msgf("client %s register to nat server %s success", client.config.ClientID, client.config.TunnelAddr)
} }
} }
func (client *Client) handleNatEvent() { func (client *Client) handleNatEvent() {
for { for {
reader := bufio.NewReader(client.natTunnel.Load().(net.Conn)) reader := bufio.NewReader(client.natTunnel.Load().(net.Conn))
// todo 支持 timeout检测
majoraPacket, err := client.Codec.Decode(reader) majoraPacket, err := client.Codec.Decode(reader)
if errors.Is(err, io.EOF) { if errors.Is(err, io.EOF) {
// 清理本地session
client.connStore = sync.Map{}
client.reConnect() client.reConnect()
continue continue
} }
...@@ -76,7 +98,7 @@ func (client *Client) handleNatEvent() { ...@@ -76,7 +98,7 @@ func (client *Client) handleNatEvent() {
continue continue
} }
logger.Debug().Msgf("receive packet type %s", majoraPacket.Ttype.ToString()) logger.Debug().Msgf("receive HeartbeatPacket type %s", majoraPacket.Ttype.ToString())
switch majoraPacket.Ttype { switch majoraPacket.Ttype {
case protocol.TypeHeartbeat: case protocol.TypeHeartbeat:
client.handleHeartbeatMessage() client.handleHeartbeatMessage()
...@@ -93,51 +115,3 @@ func (client *Client) handleNatEvent() { ...@@ -93,51 +115,3 @@ func (client *Client) handleNatEvent() {
} }
} }
} }
func (client *Client) reConnect() {
// 已经check 过
hostPort := client.Options.NatHostPort
var (
conn net.Conn
err error
)
for {
conn, err = net.DialTimeout(common.TCP, hostPort, common.ConnTimeout)
if err != nil || conn == nil {
// 不断重试
logger.Info().Msgf("reconnect to nathost with error %+v ...", err)
time.Sleep(common.ReConnInterval)
} else {
break
}
}
logger.Info().Msgf("reconnect to nathost %s success ...", hostPort)
client.natTunnel.Store(conn)
client.register()
}
func (client *Client) connect() {
hostPort := client.Options.NatHostPort
if len(hostPort) == 0 {
panic("invalid nat host/port info")
}
dialer := net.Dialer{
Timeout: common.ConnTimeout,
}
if client.Options.LocalAddr != nil {
dialer.LocalAddr = client.Options.LocalAddr
}
conn, err := dialer.Dial(common.TCP, hostPort)
if err != nil || conn == nil {
panic(fmt.Sprintf("connect to nathost %s with err %s", hostPort, err.Error()))
}
logger.Info().Msgf("connect from %s to nathost %s success ...", dialer.LocalAddr.String(), hostPort)
client.natTunnel.Store(conn)
}
package client
import (
"fmt"
"net"
"sync"
"time"
"virjar.com/majora-go/common"
"virjar.com/majora-go/infra"
"virjar.com/majora-go/logger"
"virjar.com/majora-go/protocol"
)
func (client *Client) connect() {
hostPort := client.config.TunnelAddr
if len(hostPort) == 0 {
panic("invalid nat host/port info")
}
dialer := net.Dialer{
Timeout: common.ConnTimeout,
}
if client.localAddr != nil {
dialer.LocalAddr = client.localAddr
}
conn, err := dialer.Dial(common.TCP, hostPort)
if err != nil || conn == nil {
panic(fmt.Sprintf("connect to nathost %s with err %s", hostPort, err.Error()))
}
if dialer.LocalAddr != nil {
logger.Info().Msgf("connect from %s to nathost %s success ...", dialer.LocalAddr.String(), hostPort)
} else {
logger.Info().Msgf("connect to nathost %s success ...", hostPort)
}
client.natTunnel.Store(conn)
}
func (client *Client) reConnect() {
// 已经check 过
hostPort := client.config.TunnelAddr
dialer := net.Dialer{
Timeout: common.ConnTimeout,
}
if client.localAddr != nil {
dialer.LocalAddr = client.localAddr
}
var (
conn net.Conn
err error
)
for {
conn, err = dialer.Dial(common.TCP, hostPort)
if err != nil || conn == nil {
// 不断重试
logger.Info().Msgf("reconnect to nathost with error %+v ...", err)
time.Sleep(client.config.ReconnInterval)
} else {
break
}
}
logger.Info().Msgf("reconnect to nathost %s success ...", hostPort)
client.natTunnel.Store(conn)
client.connStore = sync.Map{}
client.register()
}
func (client *Client) Redial() {
go func() {
var timer *time.Timer
for {
if timer == nil {
timer = time.NewTimer(client.config.Redial.RedialDuration)
} else {
timer.Reset(client.config.Redial.RedialDuration)
}
<-timer.C
majoraPacket := protocol.TypeDisconnect.CreatePacket()
if err := client.WriteAndFlush(majoraPacket); err != nil {
logger.Warn().Msgf("flush to nat server error %s", err.Error())
}
time.Sleep(client.config.Redial.WaitTime)
infra.Redial(client.config, client.cleanup)
}
}()
}
...@@ -13,11 +13,16 @@ import ( ...@@ -13,11 +13,16 @@ import (
"virjar.com/majora-go/protocol" "virjar.com/majora-go/protocol"
) )
var (
HeartbeatPacket = protocol.TypeHeartbeat.CreatePacket()
DisconnectPacket = protocol.TypeDisconnect.CreatePacket()
)
// todo 心跳超时检测
func (client *Client) handleHeartbeatMessage() { func (client *Client) handleHeartbeatMessage() {
go func() { go func() {
logger.Debug().Msg("receive heartbeat message from nat server") //logger.Debug().Msg("receive heartbeat message from nat server")
packet := protocol.TypeHeartbeat.CreatePacket() if err := client.WriteAndFlush(HeartbeatPacket); err != nil {
if err := client.WriteAndFlush(packet); err != nil {
logger.Error().Msgf("flush heart beat message error %s", err.Error()) logger.Error().Msgf("flush heart beat message error %s", err.Error())
} }
}() }()
...@@ -49,23 +54,28 @@ func (client *Client) handleConnect(packet *protocol.MajoraPacket) { ...@@ -49,23 +54,28 @@ func (client *Client) handleConnect(packet *protocol.MajoraPacket) {
Timeout: common.ConnTimeout, Timeout: common.ConnTimeout,
} }
if client.Options.LocalAddr != nil { if client.localAddr != nil {
dialer.LocalAddr = client.Options.LocalAddr dialer.LocalAddr = client.localAddr
} }
conn, err = dialer.Dial(common.TCP, addr) conn, err = dialer.Dial(common.TCP, addr)
if err != nil { if err != nil {
client.closeVirtualConnection(packet, "connect to target host error "+err.Error()) client.closeVirtualConnection(packet, "connect to target host error "+err.Error())
return return
} }
client.AddConnection(packet, conn) logger.Info().Msgf("connect success to %d->%s ", packet.SerialNumber, hostPort)
client.AddConnection(packet, conn, addr)
majoraPacket := protocol.TypeConnectReady.CreatePacket() majoraPacket := protocol.TypeConnectReady.CreatePacket()
majoraPacket.SerialNumber = packet.SerialNumber majoraPacket.SerialNumber = packet.SerialNumber
majoraPacket.Extra = client.Options.ClientID majoraPacket.Extra = client.config.ClientID
if err := client.WriteAndFlush(majoraPacket); err != nil { if err := client.WriteAndFlush(majoraPacket); err != nil {
logger.Error().Msgf("handleConnect message error %s", err.Error()) logger.Error().Msgf("handleConnect message error %s", err.Error())
// close && clean
_ = conn.Close() _ = conn.Close()
client.removeConnection(packet, "client:"+err.Error())
return return
} }
client.handleConnection(conn, packet) client.handleConnection(conn, packet)
...@@ -82,30 +92,51 @@ func (client *Client) WriteAndFlush(packet *protocol.MajoraPacket) error { ...@@ -82,30 +92,51 @@ func (client *Client) WriteAndFlush(packet *protocol.MajoraPacket) error {
return writer.Flush() return writer.Flush()
} }
func (client *Client) WriteAndFlushBytes(packets []byte) error {
writer := bufio.NewWriter(client.natTunnel.Load().(net.Conn))
if _, err := writer.Write(packets); err != nil {
logger.Warn().Msgf("write to nat server error err:%+v", err)
return err
}
return writer.Flush()
}
func (client *Client) handleTransfer(packet *protocol.MajoraPacket) { func (client *Client) handleTransfer(packet *protocol.MajoraPacket) {
go func(packet *protocol.MajoraPacket) { go func(packet *protocol.MajoraPacket) {
conn, ok := client.GetConnection(packet, "handleTransfer") conn, ok := client.GetConnection(packet, "handleTransfer")
// 如何把这个错误告诉服务端
if !ok { if !ok {
client.closeVirtualConnection(packet, "")
return return
} }
writer := bufio.NewWriter(conn)
if cnt, err := conn.Write(packet.Data); err != nil { flush(client, writer, packet)
logger.Warn().Msgf("write with error cnt=%d|err=%+v", cnt, err)
client.removeConnection(packet, "write_error")
}
}(packet) }(packet)
} }
func flush(client *Client, writer *bufio.Writer, packet *protocol.MajoraPacket) {
if cnt, err := writer.Write(packet.Data); err != nil {
logger.Warn().Msgf("write with error cnt=%d|err=%+v", cnt, err)
client.removeConnection(packet, "write_error")
}
if err := writer.Flush(); err != nil {
logger.Warn().Msgf("flush with error err=%+v", err)
client.removeConnection(packet, "write_error")
}
}
func (client *Client) handleConnection(conn net.Conn, packet *protocol.MajoraPacket) { func (client *Client) handleConnection(conn net.Conn, packet *protocol.MajoraPacket) {
logger.Debug().Msg("handleConnection start...") logger.Info().Msgf("serialNum %d -> handleConnection start...", packet.SerialNumber)
reader := bufio.NewReader(conn) reader := bufio.NewReader(conn)
for { for {
if _, err := reader.Peek(1); err != nil { if _, err := reader.Peek(1); err != nil {
if !errors.Is(err, net.ErrClosed) && !errors.Is(err, io.EOF) { if !errors.Is(err, net.ErrClosed) && !errors.Is(err, io.EOF) {
logger.Error().Msgf("handleConnection peek with error:%+v", err) logger.Error().Msgf("%d -> handleConnection peek with error:%+v", packet.SerialNumber, err)
} }
client.removeConnection(packet, "peek_error") bufsize := reader.Buffered()
client.removeConnection(packet, fmt.Sprintf("%d->peek_with_error:%s,bufferSize:%d",
packet.SerialNumber, err, bufsize))
break break
} }
bufsize := reader.Buffered() bufsize := reader.Buffered()
...@@ -148,9 +179,9 @@ func (client *Client) handleDestroyMessage() { ...@@ -148,9 +179,9 @@ func (client *Client) handleDestroyMessage() {
}() }()
} }
func (client *Client) AddConnection(packet *protocol.MajoraPacket, conn net.Conn) { func (client *Client) AddConnection(packet *protocol.MajoraPacket, conn net.Conn, addr string) {
client.connStore.Store(packet.SerialNumber, conn) client.connStore.Store(packet.SerialNumber, conn)
logger.Debug().Msgf("create connection for %d", packet.SerialNumber) logger.Info().Msgf("create connection for %d->%s", packet.SerialNumber, addr)
} }
// removeConnection 1. 本地缓存删除 2. 关闭连接 3. 通知natserver // removeConnection 1. 本地缓存删除 2. 关闭连接 3. 通知natserver
...@@ -169,7 +200,7 @@ func (client *Client) removeConnection(packet *protocol.MajoraPacket, reason str ...@@ -169,7 +200,7 @@ func (client *Client) removeConnection(packet *protocol.MajoraPacket, reason str
logger.Debug().Msgf("removeConnection target connection %d, reason %s", packet.SerialNumber, reason) logger.Debug().Msgf("removeConnection target connection %d, reason %s", packet.SerialNumber, reason)
majoraPacket := protocol.TypeDisconnect.CreatePacket() majoraPacket := protocol.TypeDisconnect.CreatePacket()
majoraPacket.SerialNumber = packet.SerialNumber majoraPacket.SerialNumber = packet.SerialNumber
majoraPacket.Data = []byte(client.Options.ClientID) majoraPacket.Data = []byte(client.config.ClientID)
if err := client.WriteAndFlush(majoraPacket); err != nil { if err := client.WriteAndFlush(majoraPacket); err != nil {
logger.Warn().Msgf("flush to nat server error %s", err.Error()) logger.Warn().Msgf("flush to nat server error %s", err.Error())
...@@ -184,6 +215,7 @@ func (client *Client) GetConnection(packet *protocol.MajoraPacket, step string) ...@@ -184,6 +215,7 @@ func (client *Client) GetConnection(packet *protocol.MajoraPacket, step string)
// 是否需要主动创建一个 // 是否需要主动创建一个
if !ok || load == nil { if !ok || load == nil {
logger.Warn().Msgf("can not find connection for %s->%d", step, packet.SerialNumber) logger.Warn().Msgf("can not find connection for %s->%d", step, packet.SerialNumber)
client.closeVirtualConnection(packet, "GetConnection with empty")
return nil, false return nil, false
} }
conn, ok = load.(net.Conn) conn, ok = load.(net.Conn)
...@@ -194,7 +226,7 @@ func (client *Client) closeVirtualConnection(packet *protocol.MajoraPacket, msg ...@@ -194,7 +226,7 @@ func (client *Client) closeVirtualConnection(packet *protocol.MajoraPacket, msg
logger.Warn().Msgf("disconnect to server %s", msg) logger.Warn().Msgf("disconnect to server %s", msg)
majoraPacket := protocol.TypeDisconnect.CreatePacket() majoraPacket := protocol.TypeDisconnect.CreatePacket()
majoraPacket.SerialNumber = packet.SerialNumber majoraPacket.SerialNumber = packet.SerialNumber
majoraPacket.Extra = client.Options.ClientID majoraPacket.Extra = client.config.ClientID
if err := client.WriteAndFlush(packet); err != nil { if err := client.WriteAndFlush(packet); err != nil {
logger.Error().Msgf("closeVirtualConnection with error %+v", err) logger.Error().Msgf("closeVirtualConnection with error %+v", err)
......
package client
import (
"virjar.com/majora-go/logger"
"virjar.com/majora-go/protocol"
)
const (
ActionExecShell = "executeShell"
ActionRedial = "redial"
ACTION = "action"
KeyFailedMsg = "errorMsg"
KeyStatusCode = "status"
KeyData = "data"
)
type Callback interface {
OnCmdResponse(bool, map[string]string)
}
type CmdHandler interface {
Action() string
Handle(param map[string]string, callback Callback)
}
type CmdResponse struct {
SerialNumber int64
Client *Client
}
func (c *CmdResponse) OnCmdResponse(_ bool, response map[string]string) {
packet := protocol.TypeControl.CreatePacket()
packet.SerialNumber = c.SerialNumber
packet.Data = protocol.EncodeExtra(response)
if err := c.Client.WriteAndFlush(packet); err != nil {
logger.Error().Msgf("OnCmdResponse error %+v", err)
}
}
//func OnRedialCmdResponse(client *Client, serialNumber int64, success bool, response map[string]string) {
// packet := protocol.TypeControl.CreatePacket()
// packet.SerialNumber = serialNumber
// packet.Data = protocol.EncodeExtra(response)
// if err := client.WriteAndFlush(packet); err != nil {
// logger.Error().Msgf("OnCmdResponse error %+v", err)
// }
// infra.Redial(client.config, client.cleanup)
//}
package client
import (
"virjar.com/majora-go/protocol"
)
var (
handlers = make(map[string]CmdHandler, 2)
)
func init() {
handlers[shellCmd.Action()] = shellCmd
handlers[redialCmd.Action()] = redialCmd
}
type CmdHandlerManager struct{}
func (CmdHandlerManager) HandleCmdMessage(client *Client, packet *protocol.MajoraPacket) {
param := protocol.DecodeExtra(packet.Data)
action, ok := param[ACTION]
hook := &CmdResponse{
SerialNumber: packet.SerialNumber,
Client: client,
}
if !ok || len(action) == 0 {
hook.OnCmdResponse(false, map[string]string{
KeyFailedMsg: "no param: {action} present",
KeyStatusCode: "-1",
})
return
}
cmdHandler, ok := handlers[action]
if !ok || cmdHandler == nil {
hook.OnCmdResponse(false, map[string]string{
KeyFailedMsg: "no action: " + action + " defined",
KeyStatusCode: "-1",
})
return
}
go func() {
cmdHandler.Handle(param, hook)
}()
}
package client
import (
"net"
"virjar.com/majora-go/common"
)
type (
Extra map[string]string
Options struct {
NatHostPort string
ClientID string
Account string
LocalAddr net.Addr
ExtraInfo Extra
}
Option func(*Options)
)
func WithNatServerAddr(natHostPort string) Option {
return func(options *Options) {
options.NatHostPort = natHostPort
}
}
func WithClientID(clientID string) Option {
return func(options *Options) {
options.ClientID = clientID
}
}
func WithAccount(account string) Option {
return func(options *Options) {
options.ExtraInfo[common.ExtrakeyUser] = account
}
}
func WithLocalIP(ip string) Option {
return func(options *Options) {
options.LocalAddr = &net.TCPAddr{
IP: net.ParseIP(ip),
Port: 0,
}
}
}
package client
type RedialCmd struct {
}
func (r RedialCmd) Action() string {
return ActionRedial
}
func (r RedialCmd) Handle(param map[string]string, callback Callback) {
panic("implement me")
}
var (
redialCmd = &RedialCmd{}
)
package client
import (
"os/exec"
"strings"
"virjar.com/majora-go/logger"
)
var (
cmdErrorMap = map[string]string{
KeyFailedMsg: "no param:{cmd} present",
KeyStatusCode: "-1",
}
)
var (
shellCmd = &ShellCmd{}
)
type ShellCmd struct {}
func (e *ShellCmd) Action() string {
return ActionExecShell
}
func (e *ShellCmd) Handle(param map[string]string, callback Callback) {
targetCmd := param["cmd"]
if len(targetCmd) == 0 || len(strings.TrimSpace(targetCmd)) == 0 {
callback.OnCmdResponse(false, cmdErrorMap)
return
}
logger.Info().Msgf("exec cmd %s", targetCmd)
trueCmd := strings.Split(targetCmd, " ")
cmd := exec.Command(trueCmd[0], trueCmd[1:]...) //nolint:gosec
out, err := cmd.CombinedOutput()
if err != nil {
logger.Error().Msgf("exec error %+v", err)
return
}
callback.OnCmdResponse(true, map[string]string{
KeyData: string(out),
KeyStatusCode: "0",
})
}
...@@ -21,13 +21,30 @@ const ( ...@@ -21,13 +21,30 @@ const (
) )
const ( const (
DNSServer = "114.114.114.114:53"
)
const (
UpdateServer = "http://81.70.224.147:10010"
Latest = "latest.txt"
UpdateBinaryPath = "/version/"
// VersionTpl majora-v0.0.1-linux-arm64 name-version-os-arch
VersionTpl = "%s-%s-%s-%s"
)
const (
DefaultMode = 0755
)
const (
DefNatServerHost = "majora.virjar.com" DefNatServerHost = "majora.virjar.com"
DefNatServerPort = 5879
DefNatAddr = "majora.virjar.com:5879" DefNatAddr = "majora.virjar.com:5879"
PprofAddr = "127.0.0.1:6060" PprofAddr = "127.0.0.1:6060"
) )
const ( const (
ConnTimeout = time.Second * 10 ConnTimeout = time.Second * 30
ReConnInterval = time.Second * 5 ReConnInterval = time.Second * 5
) )
......
package common
const (
DNSServer = "114.114.114.114:53"
)
const (
UpdateServer = "http://81.70.224.147:10010"
Latest = "latest.txt"
UpdateBinaryPath = "/version/"
// VersionTpl majora-v0.0.1-linux-arm64 name-version-os-arch
VersionTpl = "%s-%s-%s-%s"
)
const (
DefaultMode = 0755
)
tunnel_addr = aoba.vip:5879
dns_server = 114.114.114.114:53
;bind to local ip
;local_ip = 192.168.0.100
;for performance pprof 0 is close
;pprof_port = 0
disable_update = false
; default is info
log_level = 1
reconn_interval = 5s
;client_id =
[extra]
account = superman
[redial]
command = /bin/bash
exec_path = /root/ppp_auto_with_auth.sh
redial_duration = 5m
wait_time = 10s
\ No newline at end of file
#!/bin/bash
CURDIR=$(cd $(dirname "$0");pwd)
CONF=$CURDIR/majora.ini
chmod +x majora
echo "$CURDIR"/majora -conf "$CONF"
exec "$CURDIR"/majora -conf "$CONF"
\ No newline at end of file
...@@ -6,4 +6,7 @@ require ( ...@@ -6,4 +6,7 @@ require (
github.com/blang/semver/v4 v4.0.0 github.com/blang/semver/v4 v4.0.0
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/rs/zerolog v1.25.0 github.com/rs/zerolog v1.25.0
gopkg.in/ini.v1 v1.63.2
) )
require github.com/stretchr/testify v1.7.0 // indirect
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.25.0 h1:Rj7XygbUHKUlDPcVdoLyR91fJBsduXj5fRxyqIQj/II= github.com/rs/zerolog v1.25.0 h1:Rj7XygbUHKUlDPcVdoLyR91fJBsduXj5fRxyqIQj/II=
github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI= github.com/rs/zerolog v1.25.0/go.mod h1:7KHcEGe0QZPOm2IE4Kpb5rTh6n1h2hIgS5OOnu1rUaI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
...@@ -31,3 +38,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= ...@@ -31,3 +38,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.63.2 h1:tGK/CyBg7SMzb60vP1M03vNZ3VDu3wGQJwn7Sxi9r3c=
gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
package infra
import (
"os"
"os/exec"
"time"
"virjar.com/majora-go/logger"
)
func Restart() {
cmd := exec.Command(os.Args[0], os.Args[1:]...) //nolint:gosec
cmd.Env = os.Environ()
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
logger.Debug().Msgf("Restart ... %+v", cmd)
if err := cmd.Run(); err != nil {
logger.Error().Msgf("Restart error %+v", err)
}
}
func RestartBySignal(signal chan struct{}) {
go func() {
time.Sleep(time.Second * 5)
logger.Info().Msgf("=============cleanup==============")
signal <- struct{}{}
}()
cmd := exec.Command(os.Args[0], os.Args[1:]...) //nolint:gosec
cmd.Env = os.Environ()
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
logger.Info().Msgf("[RestartBySignal] ... %+v", cmd)
if err := cmd.Run(); err != nil {
logger.Error().Msgf("Restart error %+v", err)
}
}
package infra
import (
"os/exec"
"virjar.com/majora-go/logger"
"virjar.com/majora-go/model"
)
func Redial(cfg *model.Configure, cleanup chan struct{}) {
redial(cfg)
RestartBySignal(cleanup)
}
func redial(cfg *model.Configure) {
execPath := cfg.Redial.ExecPath
if len(execPath) == 0 {
logger.Error().Msgf("redial exec file is empty")
return
}
command := cfg.Redial.Command
if len(command) == 0 {
logger.Error().Msgf("redial command is empty")
return
}
cmd := exec.Command(command, "-c", execPath)
output, err := cmd.Output()
if err != nil {
logger.Error().Msgf("Execute Shell:%s failed with error:%s", command, err.Error())
return
}
logger.Info().Msgf("[redial] redial success %+v resp:%s", cmd, string(output))
}
package infra
import (
"testing"
"virjar.com/majora-go/model"
)
func TestRedial(t *testing.T) {
conf := model.InitConf("/Users/weixuan/code/gcode/majora-go/conf/majora.ini")
redial(conf)
}
package updater package infra
import ( import (
"errors" "errors"
...@@ -7,7 +7,6 @@ import ( ...@@ -7,7 +7,6 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"os/exec"
"runtime" "runtime"
"strings" "strings"
...@@ -72,18 +71,6 @@ func rename(src, dst string) error { ...@@ -72,18 +71,6 @@ func rename(src, dst string) error {
return nil return nil
} }
func restart() {
cmd := exec.Command(os.Args[0], os.Args[1:]...) //nolint:gosec
cmd.Env = os.Environ()
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
logger.Debug().Msgf("restart ... %+v", cmd)
if err := cmd.Run(); err != nil {
logger.Error().Msgf("restart error %+v", err)
}
}
func UpdateCore(name, latestVer, targetFile string) (bool, error) { func UpdateCore(name, latestVer, targetFile string) (bool, error) {
tf, err := ioutil.TempFile("", "") tf, err := ioutil.TempFile("", "")
if err != nil { if err != nil {
...@@ -104,7 +91,7 @@ func UpdateCore(name, latestVer, targetFile string) (bool, error) { ...@@ -104,7 +91,7 @@ func UpdateCore(name, latestVer, targetFile string) (bool, error) {
if err := rename(tf.Name(), targetFile); err != nil { if err := rename(tf.Name(), targetFile); err != nil {
return false, err return false, err
} }
restart() Restart()
return true, nil return true, nil
} }
...@@ -134,6 +121,9 @@ func getLatestVersion() (string, error) { ...@@ -134,6 +121,9 @@ func getLatestVersion() (string, error) {
} }
func needUpdate(curVer string) (bool, string) { func needUpdate(curVer string) (bool, string) {
if len(curVer) == 0 {
return false, ""
}
latestVer, err := getLatestVersion() latestVer, err := getLatestVersion()
if err != nil { if err != nil {
logger.Error().Msgf("getLatestVersion with error %s", err.Error()) logger.Error().Msgf("getLatestVersion with error %s", err.Error())
...@@ -162,6 +152,12 @@ func needUpdate(curVer string) (bool, string) { ...@@ -162,6 +152,12 @@ func needUpdate(curVer string) (bool, string) {
// Update update to the latest version // Update update to the latest version
func Update(name, curVer string) (bool, error) { func Update(name, curVer string) (bool, error) {
defer func() {
if err := recover(); err != nil {
logger.Error().Msgf("Update with error %+v", err)
}
}()
// get current exec path // get current exec path
executable, err := os.Executable() executable, err := os.Executable()
if err != nil { if err != nil {
......
...@@ -3,18 +3,20 @@ package main ...@@ -3,18 +3,20 @@ package main
import "C" import "C"
import ( import (
"virjar.com/majora-go/client" "virjar.com/majora-go/client"
"virjar.com/majora-go/protocol" "virjar.com/majora-go/model"
) )
//export NewDefClient //export NewDefClient
func NewDefClient(asyn C.int, account *C.char) { func NewDefClient(asyn C.int, account *C.char) {
options := client.NewOptions() conf := model.NewDefMajoraConf()
newAccount := C.GoString(account) newAccount := C.GoString(account)
if len(newAccount) > 0 { if len(newAccount) > 0 {
options.Account = newAccount conf.Extra.Account = newAccount
} }
cli := &client.Client{Options: options, Codec: protocol.NewDefCodec()}
cli := client.NewCli(conf)
if int(asyn) > 0 { if int(asyn) > 0 {
go cli.StartUp() go cli.StartUp()
...@@ -25,22 +27,22 @@ func NewDefClient(asyn C.int, account *C.char) { ...@@ -25,22 +27,22 @@ func NewDefClient(asyn C.int, account *C.char) {
//export NewClientWithNatServer //export NewClientWithNatServer
func NewClientWithNatServer(addr *C.char, clientID *C.char, asyn C.int, account *C.char) { func NewClientWithNatServer(addr *C.char, clientID *C.char, asyn C.int, account *C.char) {
options := client.NewOptions() conf := model.NewDefMajoraConf()
newAddr := C.GoString(addr) newAddr := C.GoString(addr)
newClientID := C.GoString(clientID) newClientID := C.GoString(clientID)
newAccount := C.GoString(account) newAccount := C.GoString(account)
if len(newAddr) > 0 { if len(newAddr) > 0 {
options.NatHostPort = newAddr conf.TunnelAddr = newAddr
} }
if len(newClientID) > 0 { if len(newClientID) > 0 {
options.ClientID = newClientID conf.ClientID = newClientID
} }
if len(newAccount) > 0 { if len(newAccount) > 0 {
options.Account = newAccount conf.Extra.Account = newAccount
} }
cli := client.NewCli(conf)
cli := &client.Client{Options: options, Codec: protocol.NewDefCodec()}
if int(asyn) > 0 { if int(asyn) > 0 {
go cli.StartUp() go cli.StartUp()
} else { } else {
......
package model
import (
"time"
"github.com/google/uuid"
"gopkg.in/ini.v1"
"virjar.com/majora-go/common"
"virjar.com/majora-go/logger"
)
type Redial struct {
Command string `ini:"command" json:"command"`
ExecPath string `ini:"exec_path" json:"exec_path"`
RedialDuration time.Duration `ini:"redial_duration" json:"redial_duration"`
WaitTime time.Duration `ini:"wait_time" json:"wait_time"`
}
type Extra struct {
Account string `ini:"account" json:"account"`
}
type Configure struct {
DisableUpdate bool `ini:"disable_update" json:"disable_update"`
LogLevel int `ini:"log_level" json:"log_level"`
PprofPort int `ini:"pprof_port" json:"pprof_port"`
TunnelAddr string `ini:"tunnel_addr" json:"tunnel_addr"`
DNSServer string `ini:"dns_server" json:"dns_server"`
LocalAddr string `ini:"local_ip" json:"local_ip"`
ReconnInterval time.Duration `ini:"reconn_interval" json:"reconn_interval"`
ClientID string `ini:"client_id" json:"client_id"`
Extra Extra `ini:"extra" json:"extra"`
Redial Redial `ini:"redial" json:"redial"`
}
const (
reconninterval = time.Second * 10
)
func NewDefMajoraConf() *Configure {
return &Configure{
DisableUpdate: false,
LogLevel: 1,
PprofPort: 0,
TunnelAddr: common.DefNatAddr,
DNSServer: common.DNSServer, //nolint:typecheck
ReconnInterval: reconninterval,
ClientID: uuid.New().String(),
Extra: Extra{
Account: uuid.New().String(),
},
Redial: Redial{
RedialDuration: reconninterval,
},
}
}
func InitConf(path string) *Configure {
conf := NewDefMajoraConf()
if err := ini.MapTo(conf, path); err != nil {
logger.Error().Msgf("InitConf with error %s, use default...", err.Error())
}
return conf
}
func (r Redial) Invalid() bool {
if len(r.Command) == 0 {
return false
}
if len(r.ExecPath) == 0 {
return false
}
if r.RedialDuration == 0 {
return false
}
return true
}
package model
import (
"testing"
"github.com/google/uuid"
)
func TestInitConf(t *testing.T) {
conf := InitConf("/Users/weixuan/code/gcode/majora-go/conf/majora.ini")
t.Logf("conf %+v", conf)
t.Logf("%s", uuid.NewString())
}
This diff is collapsed.
...@@ -2,6 +2,8 @@ package protocol ...@@ -2,6 +2,8 @@ package protocol
import ( import (
"bytes" "bytes"
"virjar.com/majora-go/logger"
) )
func EncodeExtra(extra map[string]string) []byte { func EncodeExtra(extra map[string]string) []byte {
...@@ -21,3 +23,28 @@ func EncodeExtra(extra map[string]string) []byte { ...@@ -21,3 +23,28 @@ func EncodeExtra(extra map[string]string) []byte {
} }
return buf.Bytes() return buf.Bytes()
} }
func DecodeExtra(input []byte) map[string]string {
if len(input) < 4 {
return map[string]string{}
}
buffer := bytes.NewBuffer(input)
headerSize, err := buffer.ReadByte()
if err != nil {
logger.Error().Msgf("DecodeExtra error %+v", err)
return map[string]string{}
}
data := make(map[string]string)
for i := 0; i < int(headerSize); i++ {
keyLen, _ := buffer.ReadByte()
keyBs := make([]byte, keyLen)
_, _ = buffer.Read(keyBs)
valLen, _ := buffer.ReadByte()
valBs := make([]byte, valLen)
_, _ = buffer.Read(valBs)
data[string(keyBs)] = string(valBs)
}
return data
}
package protocol
import (
"reflect"
"testing"
)
func TestEncodeExtra(t *testing.T) {
data := make(map[string]string)
data["hello"] = "world"
data["1"] = "world1"
data["2"] = "world2"
data["3"] = "world3"
data["account"] = "superman"
extra := EncodeExtra(data)
decodeExtra := DecodeExtra(extra)
t.Logf("TestEncodeExtra %+v", decodeExtra)
t.Logf("equal %v", reflect.DeepEqual(data, decodeExtra))
}
# v0.0.3
- [x] 重播 功能
- [x] 执行脚本 执行间隔
- [ ] http2 fix 抓包
- [x] 重连时间 可配置
- [x] 线程安全check
- [x] 支持config 文件
- [x] 代码的bug fix write/flush
- [ ] control
\ No newline at end of file
#!/usr/bin/env bash
TOKEN=Aau6PvRI4o0OTYrgpQHzxG7qEDkADx6CaUAJV2
TOKENONLINE=456734sdlasysdhf293r23r
make
if [ $? -ne 0 ]; then
echo "make error"
exit 1
fi
majora_version=`./bin/majora version`
echo "build version: $majora_version"
# cross_compiles
make -f ./Makefile.cross
rm -rf ./release/packages
mkdir -p ./release/packages
os_all='linux windows darwin freebsd'
arch_all='386 amd64 arm arm64 mips64 mips64le mips mipsle'
cd ./release
for os in $os_all; do
for arch in $arch_all; do
majora_dir_name="majora-cli_${majora_version}_${os}_${arch}"
majora_path="./packages/majora-cli_${majora_version}_${os}_${arch}"
if [ "x${os}" = x"windows" ]; then
if [ ! -f "./majora_${os}_${arch}.exe" ]; then
continue
fi
mkdir ${majora_path}
mv ./majora_${os}_${arch}.exe ${majora_path}/majora.exe
else
if [ ! -f "./majora_${os}_${arch}" ]; then
continue
fi
mkdir ${majora_path}
mv ./majora_${os}_${arch} ${majora_path}/majora
fi
cp -rf ../conf/majora.ini ${majora_path}
cp -rf ../conf/start.sh ${majora_path}
# packages
cd ./packages
if [ "x${os}" = x"windows" ]; then
zip -rq ${majora_dir_name}.zip ${majora_dir_name}
curl -F file=@"${majora_dir_name}.zip" -F token=$TOKEN http://81.70.224.147:10010/version/"$majora_version"
curl -F file=@"${majora_dir_name}.zip" -F token=$TOKENONLINE https://oss.virjar.com/majora/bin/"$majora_version"
else
tar -zcf ${majora_dir_name}.tar.gz ${majora_dir_name}
curl -F file=@"${majora_dir_name}.tar.gz" -F token=$TOKEN http://81.70.224.147:10010/version/"$majora_version"
curl -F file=@"${majora_dir_name}.tar.gz" -F token=$TOKENONLINE https://oss.virjar.com/majora/bin/"$majora_version"
fi
cd ..
rm -rf ${majora_path}
done
done
cd -
\ No newline at end of file
start.png

742 KB

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