init commit

This commit is contained in:
flswld
2022-11-20 15:38:00 +08:00
parent eda2b643b9
commit 3efed3defe
5834 changed files with 636508 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
http_port = 9005
[hk4e]
kcp_addr = "hk4e.flswld.com"
kcp_port = 22103
[logger]
level = "DEBUG"
method = "CONSOLE"
track_line = true
[air]
addr = "air"
port = 8086
service_name = "hk4e-gateway"
[light]
port = 10005
[database]
url = "mongodb://mongo:27017"
[mq]
nats_url = "nats://nats1:4222,nats://nats2:4222,nats://nats3:4222"

90
gate-hk4e/cmd/main.go Normal file
View File

@@ -0,0 +1,90 @@
package main
import (
"flswld.com/common/config"
"flswld.com/gate-hk4e-api/proto"
"flswld.com/light"
"flswld.com/logger"
"gate-hk4e/controller"
"gate-hk4e/dao"
"gate-hk4e/forward"
"gate-hk4e/mq"
"gate-hk4e/net"
"gate-hk4e/rpc"
"github.com/arl/statsviz"
"net/http"
_ "net/http/pprof"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
filePath := "./application.toml"
config.InitConfig(filePath)
logger.InitLogger()
logger.LOG.Info("gate hk4e start")
go func() {
// 性能检测
err := statsviz.RegisterDefault()
if err != nil {
logger.LOG.Error("statsviz init error: %v", err)
}
err = http.ListenAndServe("0.0.0.0:2345", nil)
if err != nil {
logger.LOG.Error("perf debug http start error: %v", err)
}
}()
db := dao.NewDao()
// 用户服务
rpcUserConsumer := light.NewRpcConsumer("annie-user-app")
_ = controller.NewController(db, rpcUserConsumer)
kcpEventInput := make(chan *net.KcpEvent)
kcpEventOutput := make(chan *net.KcpEvent)
protoMsgInput := make(chan *net.ProtoMsg, 10000)
protoMsgOutput := make(chan *net.ProtoMsg, 10000)
netMsgInput := make(chan *proto.NetMsg, 10000)
netMsgOutput := make(chan *proto.NetMsg, 10000)
connectManager := net.NewKcpConnectManager(protoMsgInput, protoMsgOutput, kcpEventInput, kcpEventOutput)
connectManager.Start()
forwardManager := forward.NewForwardManager(db, protoMsgInput, protoMsgOutput, kcpEventInput, kcpEventOutput, netMsgInput, netMsgOutput)
forwardManager.Start()
gameServiceConsumer := light.NewRpcConsumer("game-hk4e-app")
rpcManager := rpc.NewRpcManager(forwardManager)
rpcMsgProvider := light.NewRpcProvider(rpcManager)
messageQueue := mq.NewMessageQueue(netMsgInput, netMsgOutput)
messageQueue.Start()
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
logger.LOG.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
logger.LOG.Info("gate hk4e exit")
messageQueue.Close()
rpcMsgProvider.CloseRpcProvider()
gameServiceConsumer.CloseRpcConsumer()
rpcUserConsumer.CloseRpcConsumer()
db.CloseDao()
time.Sleep(time.Second)
return
case syscall.SIGHUP:
default:
return
}
}
}

1
gate-hk4e/cmd/static/.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* binary

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

@@ -0,0 +1,15 @@
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDHPnAvEbJfMUwHXmRLiNDH1qFeGm0U/D6n7BjzEmJl5VtMKBZF
hnz+aKsyMo9aAowi2Fe/6iWUuzcbnAJS+4iLUxaeqOdvPe5LuR3wQxHKGJ8XsDkH
kt3T1operE1rpw9wX3xuUi0CA5aHqpC0ho0zMsk7nvxWQogv1G8uqcXmfQIDAQAB
AoGBALElFmEC/vAbyFkU119A+T9z2GzuWeW6j4qFI3mZ8tpdnVqMmaCe/irDrNIo
mcORWD7y0rHS4C7odQqbHoXhFXgXfrGJXcMu977uIxBKGj0UBz6YIciznk/8DrMo
o3q6+SGsNj5zvlU8oY6cpfC663VoQb7VWveUGN4zshdnvyiRAkEA8nlq/LEuQPCj
lp5wbUizJ3Uwll5N51N6Kzm1wRQ0vUtIzRK940lGMxlhihnJfifTColAnnzmWj/X
dWIULqIc0wJBANJbrnq1iim9Jue0UOhQn6hV8vvWHgLjK7zuEsUPDqzxfhmpmBEh
BwAaH3li6bGCbIfSJazs+LmNLB4YtMo6nW8CQAMtmjxjqiKJxOslen3ENSzwOUnP
RKAilPhaEkrMlABjKzoc48ZF4Jis3X1s5xozNW3u7JznMDHAondUaMVPtKcCQG45
9lp8aBJo+ErvlHm3TYHiz7kgwIcYzKFqStGRi0oaHM6LrJBFMyrdhWKQ7w3B3ubo
ui872TU5gUWgApP5VOcCQQDDvU76TpLQ2v2LO8D0L/Ds+t6HdGcPpKvlAm/YQYHL
X6Q435tFNbeWo3JzpGElb25zAQfXU5cvzYvg37f36iM6
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,5 @@
{"remoteName": "blocks/00/22551915.blk", "md5": "0cf3d6b599d443c7cfa4a3e6189a7757", "fileSize": 3184}
{"remoteName": "blocks/00/25060239.blk", "md5": "f3cf18d697e3380b2f833ccf2c7d4194", "fileSize": 3328}
{"remoteName": "blocks/00/29342328.blk", "md5": "3b8e2c23e33ce92f68e40196c574ae94", "fileSize": 14103}
{"remoteName": "blocks/00/32070509.blk", "md5": "4bf2a81d7565301269d1c90e14393045", "fileSize": 698}
{"remoteName": "blocks/00/33067900.blk", "md5": "0b4d781c1633537d2fa4b4f0f567d2ca", "fileSize": 454}

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
ElIKBm9zX3VzYRIHQW1lcmljYRoKREVWX1BVQkxJQyIzaHR0cHM6Ly9vc3VzYWRpc3BhdGNoLnl1YW5zaGVuLmNvbS9xdWVyeV9jdXJfcmVnaW9uElMKB29zX2V1cm8SBkV1cm9wZRoKREVWX1BVQkxJQyI0aHR0cHM6Ly9vc2V1cm9kaXNwYXRjaC55dWFuc2hlbi5jb20vcXVlcnlfY3VyX3JlZ2lvbhJRCgdvc19hc2lhEgRBc2lhGgpERVZfUFVCTElDIjRodHRwczovL29zYXNpYWRpc3BhdGNoLnl1YW5zaGVuLmNvbS9xdWVyeV9jdXJfcmVnaW9uElUKBm9zX2NodBIKVFcsIEhLLCBNTxoKREVWX1BVQkxJQyIzaHR0cHM6Ly9vc2NodGRpc3BhdGNoLnl1YW5zaGVuLmNvbS9xdWVyeV9jdXJfcmVnaW9uKpwQRWMyYhAAAABbrAvbhfIRHfaSCN24qQyVAAgAAMs68ZiMdPfEj41O2wBCYqGiC/WdovvJvaw4t3/m1zIYDrt3/ftK9GKFb7C+2E8FmaHqOnwjJYBg2wI1sXpGmuSxkeWw8Avr36wlNtQjhXNV9zoNKstuZYuheyLlpbPRbYZ3UA6/BzTVsjIhjR1lcqFrigQnpV6MgRR9KqxakCaffK6qIzMlodx4ZPKlqseQhCiyVAvLWQSRqCRcZipzotXsmgLQbpDFtRzhgukXPjfW5dAlzMwswPuu7ZQsf1AKipI34dVQLu6gtXthGgbjn89h/79VR5AokLCPGqIV7/2s+gHfykrjDtyp5rwCcmGQqwV3gHy5LGrHl8Zm12jNd7Qcng51ydqtX4xzet6J2iMF6Dw5nPd/hTyxn+i3Ttk6fop9rbCq3iNgEw3+0cSDal1I1ThYdVnMgPhZgQkZc5/SpTaR+8vfDzRIKbSSrrPSEgLnQvWZOOugXhNdyuiaBc8rJveno7vvktmnhDUF3xWi6osj75j2KghRrdHfDR3Zuh4COrGZDRBSKHft2AvfrxaMT9O8hPzzzYk0U2iicVCDlNP/8wqaT9Vqt1kHmruLxqh377iyp0mxKfNt0+SNRzLyRoyvOar/z3AT6TU9LRoCFrkcJpVsUN+2MVeT52PfMbv5O/Nw9sqsFDlofCJJ/EknY0wDc+tNarYOhDM67/ojn/p6W3ZPBJxb2wcF1TOh9dpAeZdCGJusqhMIj5lpoW8nENTFhkEgMUv2Lh5Z6WpeOAKAu9eDpBMhlRNCccDaNYUgo6TdVDtWxtPrS3NRYqtkvb2I2SEFP0apht954oKdG3ncxyOgHRUkwgtxbCMAngzWo9+VWV3H3OlqeEOv7DdO2o0y95EvlHYb/qtosXPI2jC+6FPa+yl4xmLqcENRTUrU23dsmX3SyBEmZvML4dNeyC53B+mh7DUFtPFJFndxj2tGO9mTSDgy8eCmKG90AiJOMoxaLB2HpnDXN1sTiIcd3WraiE6ZCt4E54hKXvXHPyN52CHkxq1y/TeXHEq4X4MyHyDSRLHmzVs9pnwHM0ZLthKFNyvGfTvjiYokAWtNEuh74syt+m6Wietb6JvgibnnDj6uFKI3BbH4GUT9blsnMgug323bJ6bFvV4iESvz1fNnnUSokWQy5+fWzxPDohULgFzhDCpwov78Bp0E3t6DXSWnrUdNqpLbYKmXO1Hdbn+QH4B90p85UB1V5eSZgxPpUvZbIO4GPScil8K+dkDLdsFa1zypWNmlUN0Ns5H/iuzMuJql2QFYz+SnV1R1T+qywwqCNP9oswcLiAR3XnSacs52vd3PI9+0PZuoF6tVMWlvutsQ34IFZaAwIkdKigZcHumLBt/0KyFASBfN674n8FnHrHOQHU6oCeXkQA9kC8MtkvMb7fOLdzbTsD6SVojzZ64i9mDXxF+iLR9o52OxjIFzwLGRy/ivT/aAnHLZ3AsbnvslDjlQl2ADBFvf7xjmvFu0xlfK58TUpfVEkScFFapWJyKVybB4CRz1wKKz6n/a9581LpCVOWRsJa5p+j0zYcS2PfhmRf3RzwsDHeBjEVlIARbhxNKvmjdZyIidSdMMcsJHDRLE3bvo9kKfag0vRVKmuPLPc9FrACsz3vlkApcVQvzieHWoiP+foEvfj9+7Ti2tLfKdzVkMUmugZiZ46+7PKvIciiiuBPlyld0CCPTtTFHUOMO5dUfrUblX8K3awWiaNQFBS0J3iK08t1bgWfLhsKzsS32fRWugaqecwO9Rji9oHn+UuN8Nz9SgNxodroq9q7y/KHFxbqjCl62g25HN9zUa/s5wnIRwVAiWgTuOe3qGqjwp5m/GR8YVSSK/8mV9EL4AaF8d1uifdVA6wWSH1e/1UB8vcdU83P8ne3u1ho+Y/57WB7KnQaGaiD/108+wiAxNqMb2ex8on01VxdLKV1makXV3gzsvWaRevW8t/K11ZwYfo9g+guWADsA0JO0jWooiaupq1kNWrEheBdSRXBO7Jnb+56cTjPGwLpp7ZOHe/bSCJ4MGzPF3lK66LXhVo+rxvNjhoKVRjhGYxN4T8+AiRo3r+1KwdIGSrtODp3ri3JWAy6Eajp1Ukp9GaCbHSJFnYml84nKew7zLLe//ExQpjd4QAjMTvnbm+Ff6a1jf69QEVo0I33gI7/buwqgjiuvjeL6EYaMolKrKlHZHf/HwWbFbdID8T9aoyZJuCUd6YHaMPRAS6n5nvTwkRLlJ/f6wgyypUGZ22Bb1qGIb9SoPgSgIJkifUoewQW2EexqfoAsHXJVABLy+jp/SC4xzHZOSh42zU1k80HIgrnSOmu6T56F6gqy4Y2cZuZU8LXbO/01u8ifEz8yaXfEFSFdxE0TWl92OLKFtJZr9nNOBQQQr5FDGf6zB1/0CziG/5+PrUDgG3irzho6+7wXkc2CpxlBKOLWdjs3V/Lab6cURz1QZY4HYgUkJtm4U5OKUeO2+murlhC7SrnwyUtGrsD8NbCmI4SRHKPoeLBJQO/m3dRze5Ltr8N9IS7/ukPeOYe1O2agrmhH/JjYfz/l8Gmq8PGY+oavYp8I+2yKvGLD9kCxEgKcTeRh9AW/xPTLGsacrGKQCY+M76DfyLKxCZDiDY9xkBIKchxsMsn7FqZvRMMyJBHbqa3AKQyAN73NCSuFF5f1qDjARU/xqJFhOaKoR64c78oqh1GqOqEFbfNQIRw6WeFCGyW6v6p10uLdR7KXnR7+wub9aG992MpIBk0+gru74yO/WcA0vLdDEQIBwc+M0lmLB53ylsPtde3nliaC5ROHR1IS4LO8Q+3o0BHMr0my0bqFwwCAvZVXOFBHxXyUgrrmUTnZYVSQXNV6+MALBmmRU5yOzhhyHoEdj9YHZeyPpZkYc6DkJWCRYbFfmczNIs133KB9rlfug40w/hHa8pXyRyLaKQUMIUYEvt3Y4AQ==

View File

@@ -0,0 +1 @@
ElIKBm9zX3VzYRIHQW1lcmljYRoKREVWX1BVQkxJQyIzaHR0cHM6Ly9vc3VzYWRpc3BhdGNoLnl1YW5zaGVuLmNvbS9xdWVyeV9jdXJfcmVnaW9uElMKB29zX2V1cm8SBkV1cm9wZRoKREVWX1BVQkxJQyI0aHR0cHM6Ly9vc2V1cm9kaXNwYXRjaC55dWFuc2hlbi5jb20vcXVlcnlfY3VyX3JlZ2lvbhJRCgdvc19hc2lhEgRBc2lhGgpERVZfUFVCTElDIjRodHRwczovL29zYXNpYWRpc3BhdGNoLnl1YW5zaGVuLmNvbS9xdWVyeV9jdXJfcmVnaW9uElUKBm9zX2NodBIKVFcsIEhLLCBNTxoKREVWX1BVQkxJQyIzaHR0cHM6Ly9vc2NodGRpc3BhdGNoLnl1YW5zaGVuLmNvbS9xdWVyeV9jdXJfcmVnaW9uKpwQRWMyYhAAAABbrAvbhfIRHfaSCN24qQyVAAgAAMs68ZiMdPfEj41O2wBCYqGiC/WdovvJvaw4t3/m1zIYDrt3/ftK9GKFb7C+2E8FmaHqOnwjJYBg2wI1sXpGmuSxkeWw8Avr36wlNtQjhXNV9zoNKstuZYuheyLlpbPRbYZ3UA6/BzTVsjIhjR1lcqFrigQnpV6MgRR9KqxakCaffK6qIzMlodx4ZPKlqseQhCiyVAvLWQSRqCRcZipzotXsmgLQbpDFtRzhgukXPjfW5dAlzMwswPuu7ZQsf1AKipI34dVQLu6gtXthGgbjn89h/79VR5AokLCPGqIV7/2s+gHfykrjDtyp5rwCcmGQqwV3gHy5LGrHl8Zm12jNd7Qcng51ydqtX4xzet6J2iMF6Dw5nPd/hTyxn+i3Ttk6fop9rbCq3iNgEw3+0cSDal1I1ThYdVnMgPhZgQkZc5/SpTaR+8vfDzRIKbSSrrPSEgLnQvWZOOugXhNdyuiaBc8rJveno7vvktmnhDUF3xWi6osj75j2KghRrdHfDR3Zuh4COrGZDRBSKHft2AvfrxaMT9O8hPzzzYk0U2iicVCDlNP/8wqaT9Vqt1kHmruLxqh377iyp0mxKfNt0+SNRzLyRoyvOar/z3AT6TU9LRoCFrkcJpVsUN+2MVeT52PfMbv5O/Nw9sqsFDlofCJJ/EknY0wDc+tNarYOhDM67/ojn/p6W3ZPBJxb2wcF1TOh9dpAeZdCGJusqhMIj5lpoW8nENTFhkEgMUv2Lh5Z6WpeOAKAu9eDpBMhlRNCccDaNYUgo6TdVDtWxtPrS3NRYqtkvb2I2SEFP0apht954oKdG3ncxyOgHRUkwgtxbCMAngzWo9+VWV3H3OlqeEOv7DdO2o0y95EvlHYb/qtosXPI2jC+6FPa+yl4xmLqcENRTUrU23dsmX3SyBEmZvML4dNeyC53B+mh7DUFtPFJFndxj2tGO9mTSDgy8eCmKG90AiJOMoxaLB2HpnDXN1sTiIcd3WraiE6ZCt4E54hKXvXHPyN52CHkxq1y/TeXHEq4X4MyHyDSRLHmzVs9pnwHM0ZLthKFNyvGfTvjiYokAWtNEuh74syt+m6Wietb6JvgibnnDj6uFKI3BbH4GUT9blsnMgug323bJ6bFvV4iESvz1fNnnUSokWQy5+fWzxPDohULgFzhDCpwov78Bp0E3t6DXSWnrUdNqpLbYKmXO1Hdbn+QH4B90p85UB1V5eSZgxPpUvZbIO4GPScil8K+dkDLdsFa1zypWNmlUN0Ns5H/iuzMuJql2QFYz+SnV1R1T+qywwqCNP9oswcLiAR3XnSacs52vd3PI9+0PZuoF6tVMWlvutsQ34IFZaAwIkdKigZcHumLBt/0KyFASBfN674n8FnHrHOQHU6oCeXkQA9kC8MtkvMb7fOLdzbTsD6SVojzZ64i9mDXxF+iLR9o52OxjIFzwLGRy/ivT/aAnHLZ3AsbnvslDjlQl2ADBFvf7xjmvFu0xlfK58TUpfVEkScFFapWJyKVybB4CRz1wKKz6n/a9581LpCVOWRsJa5p+j0zYcS2PfhmRf3RzwsDHeBjEVlIARbhxNKvmjdZyIidSdMMcsJHDRLE3bvo9kKfag0vRVKmuPLPc9FrACsz3vlkApcVQvzieHWoiP+foEvfj9+7Ti2tLfKdzVkMUmugZiZ46+7PKvIciiiuBPlyld0CCPTtTFHUOMO5dUfrUblX8K3awWiaNQFBS0J3iK08t1bgWfLhsKzsS32fRWugaqecwO9Rji9oHn+UuN8Nz9SgNxodroq9q7y/KHFxbqjCl62g25HN9zUa/s5wnIRwVAiWgTuOe3qGqjwp5m/GR8YVSSK/8mV9EL4AaF8d1uifdVA6wWSH1e/1UB8vcdU83P8ne3u1ho+Y/57WB7KnQaGaiD/108+wiAxNqMb2ex8on01VxdLKV1makXV3gzsvWaRevW8t/K11ZwYfo9g+guWADsA0JO0jWooiaupq1kNWrEheBdSRXBO7Jnb+56cTjPGwLpp7ZOHe/bSCJ4MGzPF3lK66LXhVo+rxvNjhoKVRjhGYxN4T8+AiRo3r+1KwdIGSrtODp3ri3JWAy6Eajp1Ukp9GaCbHSJFnYml84nKew7zLLe//ExQpjd4QAjMTvnbm+Ff6a1jf69QEVo0I33gI7/buwqgjiuvjeL6EYaMolKrKlHZHf/HwWbFbdID8T9aoyZJuCUd6YHaMPRAS6n5nvTwkRLlJ/f6wgyypUGZ22Bb1qGIb9SoPgSgIJkifUoewQW2EexqfoAsHXJVABLy+jp/SC4xzHZOSh42zU1k80HIgrnSOmu6T56F6gqy4Y2cZuZU8LXbO/01u8ifEz8yaXfEFSFdxE0TWl92OLKFtJZr9nNOBQQQr5FDGf6zB1/0CziG/5+PrUDgG3irzho6+7wXkc2CpxlBKOLWdjs3V/Lab6cURz1QZY4HYgUkJtm4U5OKUeO2+murlhC7SrnwyUtGrsD8NbCmI4SRHKPoeLBJQO/m3dRze5Ltr8N9IS7/ukPeOYe1O2agrmhH/JjYfz/l8Gmq8PGY+oavYp8I+2yKvGLD9kCxEgKcTeRh9AW/xPTLGsacrGKQCY+M76DfyLKxCZDiDY9xkBIKchxsMsn7FqZvRMMyJBHbqa3AKQyAN73NCSuFF5f1qDjARU/xqJFhOaKoR64c78oqh1GqOqEFbfNQIRw6WeFCGyW6v6p10uLdR7KXnR7+wub9aG992MpIBk0+gru74yO/WcA0vLdDEQIBwc+M0lmLB53ylsPtde3nliaC5ROHR1IS4LO8Q+3o0BHMr0my0bqFwwCAvZVXOFBHxXyUgrrmUTnZYVSQXNV6+MALBmmRU5yOzhhyHoEdj9YHZeyPpZkYc6DkJWCRYbFfmczNIs133KB9rlfug40w/hHa8pXyRyLaKQUMIUYEvt3Y4AQ==

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAz/fyfozlDIDWG9e3Lb29+7j3c66wvUJBaBWP10rB9HTE6prj
fcGMqC9imr6zAdD9q+Gr1j7egvqgi3Da+VBAMFH92/5wD5PsD7dX8Z2f4o65Vk2n
VOY8Dl75Z/uRhg0Euwnfrved69z9LG6utmlyv6YUPAflXh/JFw7Dq6c4EGeR+Kej
FTwmVhEdzPGHjXhFmsVt9HdXRYSf4NxHPzOwj8tiSaOQA0jC4E4mM7rvGSH5GX6h
ma+7pJnl/5+rEVM0mSQvm0m1XefmuFy040bEZ/6O7ZenOGBsvvwuG3TT4FNDNzW8
Dw9ExH1l6NoRGaVkDdtrl/nFu5+a09Pm/E0ElwIDAQABAoIBAQCtH17Cck+KJQYX
j29xqG4qykNUDawbILiKCMkBE7553Wq/UcjmuuR4bVnML8ucS3mgR/BgHV3l8vUK
nxvqRx/oGZkWNazbiuwL+ThAblLWqrEmYuZVCoQcAnvkT8tIqDWz7fhDEuZnnkMz
ZcATIZzgZUSa5IfP3u3rP+MrVbyaCdzJEeI0Yrv1XT+M5ddkKQrYgqC5kRiYi/Lj
NcLJhqSVt8p37CdJx1PGHFjKKb4MZpANlNRgeTtWpGVfS0PJLzaiI1NyPSJv7xWZ
gVhbK9+wQxqSG6KmZ4vpEvRI1zKiov5BsAFN+GfuD5mpn1Xo9CpzTfj/sO13VpHH
+Mt80+yBAoGBAPYXVEcXug5zqkqXup4dp1S05saz1zWPhUhQm+CrbhgeTqpjngJJ
EB79qMrGmyki0P/cGtbTcrHf8+i7gDlIGW0OMb4/jn4f5ACVD00iyvkHSGPn0Aim
MoNOMbkGot7SkSnncwxXdawwDyTu2dofXuBr72+GYqgRAG52IuA0C0pRAoGBANhX
p/UyW/htB27frKch/rTKQKm12kBV20AkkRUQUibiiQyWueWKs+5bVaW5R5oDIhWx
qftJtnEFWUvWaTHpHsB/bpjS3CJ6WknqNbpa3QIScpV1uw8V+Etz/K2/ftjyZzFo
nqc+Jud5364xFdIlOsRj9gZnK83Wcui6EFxAer5nAoGBAJzTzzSjLUHqejqhKR98
nFeCFZPJpjuO5AxqunvaJAYgwlcZtueT8j8dvgTDvrvfYTu85CnFhNFQfFrzqspW
ZUW3hwHL9R3xatboJ2Er7Bf5iSuJ3my0pXpCSbO1Q/QmUrZWtl3GGsqJsg0CXjkA
RvFUN7ll9ddPRmwewykIYa2RAoGAcmKuWFNfE1O6WWIELH4p6LcDR3fyRI/gk+KB
nyx48zxVkAVllrsmdYFvIGd9Ny4u6F9+a3HG960HULS1/ACxFMCL3lumrsgYUvp1
m+mM7xqH4QRVeh14oZRa5hbY36YS76nMMMsI0Ny8aqJjUjADCXF81FfabkPTj79J
BS3GeEMCgYAXmFIT079QokHjJrWz/UaoEUbrNkXB/8vKiA4ZGSzMtl3jUPQdXrVf
e0ofeKiqCQs4f4S0dYEjCv7/OIijV5L24mj/Z1Q4q++S5OksKLPPAd3gX4AYbRcg
PS4rUKl1oDk/eYN0CNYC/DYV9sAv65lX8b35HYhvXISVYtwwQu/+Yg==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA02M1I1V/YvxANOvLFX8R7D8At40IlT7HDWpAW3t+tAgQ7sqj
CeYOxiXqOaaw2kJhM3HT5nZll48UmykVq45Q05J57nhdSsGXLJshtLcTg9liMEoW
61BjVZi9EPPRSnE05tBJc57iqZw+aEcaSU0awfzBc8IkRd6+pJ5iIgEVfuTluani
zhHWvRli3EkAF4VNhaTfP3EkYfr4NE899aUeScbbdLFI6u1XQudlJCPTxaISx5Zc
wM+nP3v242ABcjgUcfCbz0AY547WazK4bWP3qicyxo4MoLOoe9WBq6EuG4CuZQrz
Knq8ltSxud/6chdg8Mqp/IasEQ2TpvY78tEXDQIDAQABAoIBAQC4uPsYk4AsSe75
0Au6Dz7kSfIgdDhJ44AisvTmfLauMFZLtfxfjBDhCwTxuD7XnCZAxHm97Ty+AqSp
Km/raQQsvtWalMhBqYanzjDYMRv2niJ1vGjm3WrQxBaEF+yOtvrZsK5fQTslqInI
qknIQH7fgjazJ7Z28D18sYNj37qfFWSSymgFo+SoS/BKEr200lpRA/oaGXiHcyIO
jJidP6b7UGes7uhMXUvLrfozmCsSqslxXO5Uk5XN/fWl4LxCGX7mpNfPZIT5YBSj
HliFkNlxIjyJg8ORLGi82M2cuyxp39r93F6uaCjLtb+rdwlGur7npgXUkKfWQJf9
WE7uar6BAoGBAPXIuIuYFFUhqNz5CKU014jZu6Ql0z5ZA08V84cTJcfLIK4e2rqC
8DFTldA0FtVfOGt0V08H/x2pRChGOvUwGG5nn9Dqqh6BjByUrW4z2hnXzT3ZuSDh
6eapiCB1jl9meJ0snhF2Ps/hqWGL2b3SkCCe90qVTzOVOeLO6YUCIOq9AoGBANws
fQkAq/0xw8neRGNTrnXimvbS+VXPIF38widljubNN7DY5cIFTQJrnTBWKbuz/t9a
J8QX6TFL0ci/9vhPJoThfL12vL2kWGYgWkWRPmqaBW3yz7Hs5rt+xuH3/7A5w5vm
kEg1NZJgnsJ0rMUTu1Q6PM5CBg6OpyHY4ThBb8qRAoGAML8ciuMgtTm1yg3CPzHZ
xZSZeJbf7K+uzlKmOBX+GkAZPS91ZiRuCvpu7hpGpQ77m6Q5ZL1LRdC6adpz+wkM
72ix87d3AhHjfg+mzgKOsS1x0WCLLRBhWZQqIXXvRNCH/3RH7WKsVoKFG4mnJ9TJ
LQ8aMLqoOKzSDD/JZM3lRWkCgYA8hn5Y2zZshCGufMuQApETFxhCgfzI+geLztAQ
xHpkOEX296kxjQN+htbPUuBmGTUXcVE9NtWEF7Oz3BGocRnFrbb83odEGsmySXKH
bUYbR/v2Ham638UOBevmcqZ3a2m6kcdYEkiH1MfP7QMRqjr1DI1qpfvERLLtOxGu
xU5WAQKBgQCaVavyY6Slj3ZRQ7iKk9fHkge/bFl+zhANxRfWVOYMC8mD2gHDsq9C
IdCp1Mg0tJpWLaGgyDM1kgChZYsff4jRxHC4czvAtoPSlxWXF2nL31qJ3vk2Zzzc
a4GSHAInodXBrKstav5SIKosWKT2YysxgHlA9Sm2f4n09GjFbslEHg==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyaxqjPJP5+Innfv5IdfQqY/ftS++lnDRe3EczNkIjESWXhHS
OljEw9b9C+/BtF+fO9QZL7Z742y06eIdvsMPQKdGflB26+9OZ8AF4SpXDn3aVWGr
8+9qpB7BELRZI/Ph2FlFL4cobCzMHunncW8zTfMId48+fgHkAzCjRl5rC6XT0Yge
6+eKpXmF+hr0vGYWiTzqPzTABl44WZo3rw0yurZTzkrmRE4kR2VzkjY/rBnQAbFK
KFUKsUozjCXvSag4l461wDkhmmyivpNkK5cAxuDbsmC39iqagMt9438fajLVvYOv
pVs9ci5tiLcbBtfB4Rf/QVAkqtTm86Z0O3e7DwIDAQABAoIBAQCyma226vTW35LE
N5zXWuAg+hhcxk6bvofWMUMXKvGF/0vHPTMXlvuSkDeDNa4vBivneRthBNPMgb3q
DuTWxrogQMOOI8ZdhY3DFexfDvcQD2anDJuSqSmg9Nd36q+yxk3xIoXB5Ilo23dd
vTnJXHhsBNovv7zRLO134cAHFqDoKzt5EEHre0skUcn6HjHOek6c53jvpKr5LSrr
iwx5gMuY/7ZSIUDo9WGY70qbQFGY6bOlX9x8uNjcFF+7SztEVQ+vhJ/+7EvwqaJr
ysweo0l91TKM9WaMuwoucKeceVWuynEw6GGTw8UTLtltekLGe6bS8YxY8fVwnKkT
RwJYwAJRAoGBAP2rhcfOA+1Ja37hUHKebfp9rHsex4+pGyt3Kdu7WdqOn4sexmya
BuiHQcUchPDVla/ruQZ20+8LHgzBDo0m8sY7gpf715UV9NSVIRD0wu26SKRklOFz
J4HBOwU9hBGLSnRUJzyvVlt5O7E9hAv61SCrvWBEcow2YnKNQLwvjMVJAoGBAMuG
oSb3A/ulqtp2zpxVAclYe/bSItZZTOUWP6Vb4hOiHxIJ0n1H9ap6grOYkJ/Yn4gg
yYzKm/noF1wXP7Rj/xOahnvMkzhGdmOabvE9LH5HwQTWxBBWTkZzgBbYtbg+J5MT
cKqJaychSRjJj+xX+d90rtlSu/c27chlSRKAHXWXAoGAFTcIHDq9l1XBmL3tRXi8
h+uExlM/q2MgM5VmucrEbAPrke4D+Ec1drMBLCQDdkTWnPzg34qGlQJgA/8NYX61
ZSDK/j0AvaY1cKX8OvfNaaZftuf2j5ha4H4xmnGXnwQAORRkp62eUk4kUOFtLrdO
pcnXL7rpvZI6z4vCszpi0okCgYEAp3lZEl8g/+oK9UneKfYpSi1tlGTGFevVwozU
QpWhKta1CnraogycsnOtKWvZVi9C1xljwF7YioPY9QaMfTvroY3+K9DjM+OHd96U
fB4Chsc0pW60V10te/t+403f+oPqvLO6ehop+kEBjUwPCkQ6cQ3q8xmJYpvofoYZ
4wdZNnECgYBwG8Vrv7Z+kX9Zuh1FvcRoY57bYLU0cWW92SA3Nvi8pZOIEaLHrQyZ
pvvaLIicR1m9+KsOAmii7ru0zL7KsrGW+5migQsaDi4gzahKQpad/R7MLKi/L53r
Ymo0aZKARLHW82GbomQ0zxdRoo9vaqfGNpXkxyyt3k3GGDunmrskYw==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAsJbFp3WcsiojjdQtVnTuvtawL2m4XxK93F6lCnFwcZqUP39t
xFGGlrogHMqreyawIUN7E5shtwGzigzjW8Ly5CryBJpXP3ehNTqJS7emb+9LlC19
Oxa1eQuUQnatgcsd16DPH7kJ5JzN3vXnhvUyk4Qficdmm0uk7FRaNYFi7EJs4xyq
FTrp3rDZ0dzBHumlIeK1om7FNt6Nyivgp+UybO7kl0NLFEeSlV4S+7ofitWQsO5x
YqKAzSzz+KIRQcxJidGBlZ1JN/g5DPDpx/ztvOWYUlM7TYk6xN3focZpU0kBzAw/
rn94yW9z8jpXfzk+MvWzVL/HAcPy4ySwkay0NwIDAQABAoIBADzKWpawbVYEHaM4
lLb7oCjAPXzE9zx7djLDvisfLCdfoINPedkoe52ty1o+BtRpWB7LXTY9pFic1FLE
5wvyy6zyf8hH3ZsysqNhWFxhh4FnLmx/UGokAir+anaK5mYVJ1vQtxzjlV1HAbQs
kRyrklKoHDdRFqiFXOwiib97oDNWhD+RxfyGwwJnynZZSXdLbLSiz/QHQNr/+Ufk
KRBaxt0CfU7mOLZxoy6fNAxHdBcBJPHCyh+aDvEbix7nSncSU8Ju/48YJ8DrglbZ
sXCYoA5Uz8NMDuaEMgoNWCFQVoEcRkEUoaH7BlWd3UUFRPnDZ1B4BmkrVoRE8a58
3OqSwakCgYEA19wQUISXtpnmCrEZfbyZ6IwOy8ZCVaVUtbTjVa8UyfNglzzJG3yz
cXU3X35v5/HNCHaXbG2qcbQLThnHBA+obW3RDo+Q49V84Zh1fUNH0ONHHuC09kB/
/gHqzn/4nLf1aJ2O0NrMyrZNsZ0ZKUKQuVCqWjBOmTNUitcc8RpXZ8sCgYEA0W09
POM/It7RoVGI+cfbbgSRmzFo9kzSp5lP7iZ81bnvUMabu2nv3OeGc3Pmdh1ZJFRw
6iDM6VVbG0uz8g+f8+JT32XdqM7MJAmgfcYfTVBMiVnh330WNkeRrGWqQzB2f2Wr
+0vJjU8CAAcOWDh0oNguJ1l1TSyKxqdL8FsA38UCgYEAudt1AJ7psgOYmqQZ+rUl
H6FYLAQsoWmVIk75XpE9KRUwmYdw8QXRy2LNpp9K4z7C9wKFJorWMsh+42Q2gzyo
HHBtjEf4zPLIb8XBg3UmpKjMV73Kkiy/B4nHDr4I5YdO+iCPEy0RH4kQJFnLjEcQ
LT9TLgxh4G7d4B2PgdjYYTkCgYEArdgiV2LETCvulBzcuYufqOn9/He9i4cl7p4j
bathQQFBmSnkqGQ+Cn/eagQxsKaYEsJNoOxtbNu/7x6eVzeFLawYt38Vy0UuzFN5
eC54WXNotTN5fk2VnKU4VYVnGrMmCobZhpbYzoZhQKiazby/g60wUtW9u7xXzqOd
M/428YkCgYBwbEOx1RboH8H+fP1CAiF+cqtq4Jrz9IRWPOgcDpt2Usk1rDweWrZx
bTRlwIaVc5csIEE2X02fut/TTXr1MoXHa6s2cQrnZYq44488NsO4TAC26hqs/x/H
bVOcX13gT26SYngAHHeh7xjWJr/KgIIwvcvgvoVs6lu7a8aLUvrOag==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAxbbx2m1feHyrQ7jP+8mtDF/pyYLrJWKWAdEv3wZrOtjOZzeL
GPzsmkcgncgoRhX4dT+1itSMR9j9m0/OwsH2UoF6U32LxCOQWQD1AMgIZjAkJeJv
FTrtn8fMQ1701CkbaLTVIjRMlTw8kNXvNA/A9UatoiDmi4TFG6mrxTKZpIcTInvP
EpkK2A7Qsp1E4skFK8jmysy7uRhMaYHtPTsBvxP0zn3lhKB3W+HTqpneewXWHjCD
fL7Nbby91jbz5EKPZXWLuhXIvR1Cu4tiruorwXJxmXaP1HQZonytECNU/UOzP6GN
Ldq0eFDE4b04Wjp396551G99YiFP2nqHVJ5OMQIDAQABAoIBAQDEeYZhjyq+avUu
eSuFhOaIU4/ZhlXycsOqzpwJvzEz61tBSvrZPA5LSb9pzAvpic+7hDH94jX89+8d
NfO7qlADsVNEQJBxuv2o1MCjpCRkmBZz506IBGU60Kt1j5kwdCEergTW1q375z4w
l8f7LmSL2U6WvKcdojTVxohBkIUJ7shtmmukDi2YnMfe6T/2JuXDDL8rvIcnfr5E
MCgPQs+xLeLEGrIJdpUy1iIYZYrzvrpJwf9EJL3D0e7jkpbvAQZ8EF9YhEizJhOm
dzTqW4PgW2yUaHYd3q5QjiILy7AC+oOYoTZln3RfjPOxl+bYjeMOWlqkgtpPQkAE
4I64w8RZAoGBAPLR44pEkmTdfIIF8ZtzBiVfDZ29bT96J0CWXGVzp8x6bSu5J5jl
s7sP8DEcjGZ6vHsLGOvkcNxzcnR3l/5HOz6TIuvVuUm36b1jHltq1xZStjGeKZs1
ihhJSu2lIA+TrK8FCRnKARJ0ughXGNZFItgeM230Sgjp2RL4ISXJ724XAoGBANBy
S2RwNpUYvkCSZHSFnQM/jq1jldxw+0p4jAGpWLilEaA/8xWUnZrnCrPFF/t9llpb
dTR/dCI8ntIMAy2dH4IUHyYKUahyHSzCAUNKpS0s433kn5hy9tGvn7jyuOJ4dk9F
o1PIZM7qfzmkdCBbX3NF2TGpzOvbYGJHHC3ssVr3AoGBANHJDopN9iDYzpJTaktA
VEYDWnM2zmUyNylw/sDT7FwYRaup2xEZG2/5NC5qGM8NKTww+UYMZom/4FnJXXLd
vcyxOFGCpAORtoreUMLwioWJzkkN+apT1kxnPioVKJ7smhvYAOXcBZMZcAR2o0m0
D4eiiBJuJWyQBPCDmbfZQFffAoGBAKpcr4ewOrwS0/O8cgPV7CTqfjbyDFp1sLwF
2A/Hk66dotFBUvBRXZpruJCCxn4R/59r3lgAzy7oMrnjfXl7UHQk8+xIRMMSOQwK
p7OSv3szk96hy1pyo41vJ3CmWDsoTzGs7bcdMl72wvKemRaU92ckMEZpzAT8cEMC
cWKLb8yzAoGAMibG8IyHSo7CJz82+7UHm98jNOlg6s73CEjp0W/+FL45Ka7MF/lp
xtR3eSmxltvwvjQoti3V4Qboqtc2IPCt+EtapTM7Wo41wlLCWCNx4u25pZPH/c8g
1yQ+OvH+xOYG+SeO98Phw/8d3IRfR83aqisQHv5upo2Rozzo0Kh3OsE=
-----END RSA PRIVATE KEY-----

Binary file not shown.

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD>lt1L <09><>ܟ<EFBFBD>.<15>\<5C>pXP<58><50>"ƀ(<28>a<><61><EFBFBD>

View File

@@ -0,0 +1 @@
OPrCJaJsJJJXI3$DQNduVQuB5s5jgf47mmABUJFZoKJUt5YcIt0wPCBcCCpuQ4dQz19rae5FeHYRpmb8WsjkIeEpAQi3uJlIgObMsL$Fmv1kPav7dDPBWKJuyFEOa2KpN1EGvWlJOqufR64SDYhIocM3HLT5VsLo$Bde3c2jsVPwygAGGcZU1rZCTaOo$2blfEMdpviC75gNDZzdGjAiOBahjs$!9fZKtNF2iI4UnGSE2aNXlIz9To8L9fSeA9mhJ2F0qTKa8UjxHiSurW0bSzloUUN83sT9UFBNz$c2IVIP!asMffifiTVmlb0MJ6jYUB!rMSR77SVmdNDlMRz1VnjR9xEENS1DnqUQSYq2JJAQJIZL6j4ABF!Dm5NisAf58vaRhY4Uz6zXEf$IZExmbToDsb49X6ATh7zZExCeddR8iDBFpdOAiveE0bO8pOgo!zYmt0$5sN8aJpDK10XbXkm21$Lvppj8gAV9SjUjT5j9luqok!PkTQS59zG1I$JSDzs1jkoaNTiO2LL0rJSHaTX2EMI4KUiMp1Tgpqsy$lSv89CVFg5AzODZX8TA5ne4lHT0aazfxQa354RHYRGe5OllgcuZVn88gWjmD3nUHtzhCpK0siGWqPvOlPX!OYUaJIFwaSy7v!K5Q2nX8qCjGAHeu1$hs0cdmn69r1pws0APbZ7BNm018poX7YxjiOBRV0tEKRcJbkKxad$t5dJIO9eJhKvk0l9xz8KjOF9u2NHhy5yQrHZWenJYDAIrmheIz2Q!vO7EePv2U6ACm5OJS8dDyWBMkFNuSoW4zv3RTIfdKw$!hd2bmSFQFic6jFNPeag53tpBPCiy3lNgIT0hgytmV9CBwZl!S$GzYOxv0buJ2P2Vvfah!bg4$nfAHKoHer4zTcYN4w3eSIBAOCKJW7uJ5Sk1uOcxVwBklzbN6l7m3Yhcyw44y76P7q$zeK4NuEpaK7izZTqVhhXL1piyu5A$lqWlkxCDbq0H3haa79hX0npzRphBlt7syl3UM2MNJS7ZgYYk76kNZSO!ZRaBz19phzewChIaE2Seub1h0U3JJI7PH1a5Ej6ozVaFUCSGvULtLeQeq!drnnvnqCfK4UZmbNYrHUPBMif2ttgHygUxSRQpMHx$tstSzESKn6XuQo4k!Fuiq5Du1ClQtPE$fgyYuzl4SDkw0RJYBY5dl227vyhKQzyxrSQQUQsKy08xM2LRZUymBp$oDiWBF4ZJv8iE5fDMuwbTRSfPX45uBKiDYSYcOXukcDeYYXturEpUD4VaC0RYF5KVBos1kTT2sQFq$cbkN9jiwmhBC7Yea6LEw64GOq67dlf$yvJmw5L8Sxb!Yd2u$PX2cQh4d19LpvkLTaqhofHmYP9agwBwxLQ4z4z93gkPmgKdGir40bV$nbDNcve6juK4Ct6R$Xid6nkdWM$9RWvQE7m7S7Rf5X8dWEHn86g6cLfi4saH5jOi1abRRYlBixuz3yiRQy!IOOvoH0RVqpamMy03xqGUscyuxjrTYhT1AMBmDaj$l9qgIMN3sS5EONMxMEFXvTo1!3vNCRhuahOt5bsN7WyLpiQiKb76onr8!j9l1QDwNm74OIXSQ2T1OgQeppxh3B5vkx4c8$t8$GfBCq4Q!dDZWu!PNdbRk2sWgGnD$OVXHJgvQZPkJPdQJSm7p85tlx3sh7vCDVBauxtbHz$iXqS5HIj8PX!ZmG20iYl5$H$ZA1e$nYfeD$rafvfJDYmKH9sgLPtP2N5LqNI6SIzrihs7U0ST7iTDzJrw9tMGLFrmtEZ!G8mlzMRBLUsTMFyTa5!r043dtVxSsNdcor2xkUhvJVjjnfuyNNmbd5GoxUxhoyqb0Xx5RuwGFevo!PEZ$FHiNjLzds0LXH20$6QXhWa2k9e4VOO6Far4iutjtXFf!qMQCpeBH2TcpGbbjjziBj$pKzCYhbLnUYL8lBN6sP381fn8MgT2M4WKAXm!N83VRULxtxx7spdeQKvM5Fx6CcMThAZXDOtcoqXMWXOaXt9sY6xSsnbq4ZvrPi8s4v17p95iCpNoJY2KZP4qiWgfcxir1iOYmWIU9$DCSb3jmJz6QzCVCdvy3XKLjropmAce89vi9U4W9v7abEwdKD1!4zdUBMlhRSLo3AX4uLpdDbc7!icy$tdFrrPhBV6VvZDoYXwYbCD91z6I$thUPyk50ct9MVx$dXRXEW8rpdVavkcuae1wOLgC36naRV$P3ciba8qoqFQqewxnhBdUi9rz33lo86lS6K6PPtOD!00D0SY9m48yyjUgqF9uYFq9tAXMoZZ6LruqS5WcVHcVEw6LocLJvyWZwy!8zKFtA5whK08PbAnz2BCTnM$$YPwssO!!$l5v1qz61VXerSg6V0$$eA1JyiZRF$0ErSFtwM51gnD7qXnlIMQ8krXmuUcWdbrvTKUOsY!6t8MfYvlPZDsJz$WaJDCT2TLHqjJRlGvr7CiBncg5S1!5hS6eQAhDw3fcw3ghO5fcIeDT!doP1E0W!MPbqSzWrrGXdYbtcfj5iqShrnDkizgD3E7q8zbBMa9kL3NaCBt2UP!jOULCeTIC3LxrSmhKCgHnhrsuyPaJg6e4jGr0n1Xj1K2gKybs4AIAOyAfEPqZEt$6WSrtp$3O

View File

@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIID8zCCAtugAwIBAgIUYL/dZNSITnL3uvCDT62CVzoHeKMwDQYJKoZIhvcNAQEL
BQAwgYgxCzAJBgNVBAYTAkNOMREwDwYDVQQIDAhTaGFuZ2hhaTERMA8GA1UEBwwI
U2hhbmdoYWkxDzANBgNVBAoMBmZsc3dsZDEPMA0GA1UECwwGZmxzd2xkMQ8wDQYD
VQQDDAZmbHN3bGQxIDAeBgkqhkiG9w0BCQEWEWZsc3dsZEBmbHN3bGQuY29tMB4X
DTIyMDQyNjE3MjYwN1oXDTMyMDQyMzE3MjYwN1owgYgxCzAJBgNVBAYTAkNOMREw
DwYDVQQIDAhTaGFuZ2hhaTERMA8GA1UEBwwIU2hhbmdoYWkxDzANBgNVBAoMBmZs
c3dsZDEPMA0GA1UECwwGZmxzd2xkMQ8wDQYDVQQDDAZmbHN3bGQxIDAeBgkqhkiG
9w0BCQEWEWZsc3dsZEBmbHN3bGQuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
MIIBCgKCAQEAp4bjl/JavfVApTELsgNhGxwlXY+Z4SoVz8a+KVogdZf0fdMt62ob
YMwg7mg9r5dUmfvfvHhtFjCjviR+sKkS6Dgfc+xwS2ap4DWNwk/7kpmDr/QYTqb9
XO8kUAQ+zPxU0rfPE8KbdCvTlTh4opCMWde8C3BvWExNcKBgb132pHlZNwJTDs7G
laa1T9aSoDz+I9qb91acKnWsDm9NumQ86L1pJgZ+DylzBkVAwTHbMgURLM6hGGmW
RHaYKOnHb/s91YxgO1pN56IL6M2tZQuydt8pMC1Tt3z8yZR2nqAPVvF3j0U1Xh2+
X9iKL9zAsCXgw+nQt/s9QWFP70Pr95hQ8wIDAQABo1MwUTAdBgNVHQ4EFgQU0HwJ
oRLZWEVg9itEpy1VI04MefkwHwYDVR0jBBgwFoAU0HwJoRLZWEVg9itEpy1VI04M
efkwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAK2vphvnVGONT
GBdb8khlPGVf+2caAy5+LrgxX77wQy4aRO//e8r040awzkhN4C8eC2ozGLifzTKy
zxNKWNp163c4v2i0PGx4gdAS7yfLMklUndntiV/uPdk4jZ7Vr4eTciN+Sf0FdUUR
K7tpq0HNHrI6q+29B8Eq1ClkBYAmJqnHllu1EmpNReXkPOOaadpv2pjoJDmIWumI
wCKs4uGZburgX9jBsFHuyRDYOkG+1OGL4zKZClMuEFNodOa3zm4WrBPzWM3WOBZ9
uHLCXkRnce192OopnaytxEEoxuKCQ3lQZzrcuoz1wRfczTBh2pUU4YR1L6oBNuG+
oC9MpDAcmw==
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAp4bjl/JavfVApTELsgNhGxwlXY+Z4SoVz8a+KVogdZf0fdMt
62obYMwg7mg9r5dUmfvfvHhtFjCjviR+sKkS6Dgfc+xwS2ap4DWNwk/7kpmDr/QY
Tqb9XO8kUAQ+zPxU0rfPE8KbdCvTlTh4opCMWde8C3BvWExNcKBgb132pHlZNwJT
Ds7Glaa1T9aSoDz+I9qb91acKnWsDm9NumQ86L1pJgZ+DylzBkVAwTHbMgURLM6h
GGmWRHaYKOnHb/s91YxgO1pN56IL6M2tZQuydt8pMC1Tt3z8yZR2nqAPVvF3j0U1
Xh2+X9iKL9zAsCXgw+nQt/s9QWFP70Pr95hQ8wIDAQABAoIBAD7rNHOO/HG3uO3R
+9iB2Gi8K3R2SI7+pW6B8E3LocFIrvTK6cYu9dVnFT81O2XFamri5Gb+u8nHvtfd
EQ/8kDNTUMzTEmHfTxH8Sx+dtpiau5DMFo0Dvsi2sGa1EXkflCQIEOgVARmilDbJ
HNXBgFUF54RMWCVLkxInydBJ9CZVKUVgN5xmUTnLOEINbcn3V8d2aLwSYjECW5Gm
22QzrA05dKvmc6Io776Sc2fNDnJHTkHMrxH+kQ+c2jnHLWvdMb3aCJh/4JDAF/Iq
CZ6EsXBBYJ7C3ZRMata3LEgGU44L2oOZpUcd+RNldcvaBN8T86hxiyHddC9DC+AE
+svJIgECgYEA3H2EBQeVDjJ6PGE/elHJwRyWWtb9xxd7hbJkDfqw+SMEX2pYKnf5
tftXKbPY+U09Sm78Mbdac7W+O7IfD1e00/EDIcjdmq4S2CMGNtQ4GQxfxsFzg2LH
ujtVtx/xuvCNQbgNOEXl+/127AQpSDkhPxD2Tm1WoH72UXS82Ydq9HMCgYEAwoHF
ucrscqPRsg9AVb0UbecbJjRjQD+BudGmOkm0d/QCsjANRJYpSoQ9jP39uqYWlh4f
JAe+H89UWdDTVtFWL14+//nLNqPeCAu70RYt+xajvGAWUCRYaZvECBQW5stzHnHv
p2LG6TSyRbs10vBHxSkxgDe1Ng5+7jjJe09KkYECgYASu5c71ikTy7YW6yw5eDlr
7sHXdeyZvaUA9ucJSQNAJ3l3odFbylWs4G3HXUBR7f4HFObYUnuc2RQQflGlPA5g
81kQxcAOJDv1oQQmJGGfvy1j9Yua3gmaCPB/XndrKoTV0I1O+qFPh3lTFAdt22y3
rvk+MIvrlt3WjdR9psOvgQKBgGOvhtq1uYD3nJ0ZW+uVQEcjTrLB3qwq4B2P6RWu
eKORl2AjaGliXD8ojzMXaVajkKfXQDaDEVnUNHLjp6yzFOyp7LfcGd4jFcQh31xF
dcNd0wTUahsgxX86qblKMoKOeq17z0uGQFN9AnDiha9aHi5Z8li4NFNEEqGc0QY1
mQ4BAoGALmY+k+sL3jdjZ7XwJTuCfPka/UQP32osWzZMCvalts5I73Osyr9+o3sC
gptDMTOmUIh6xxY2pshM6i6etKgyDA7r9utUe+w6pvMIrDG1uZva/2oj0jzQrRq5
X9F3C/MJVMcVSI6Uk+rvBb8QtltKovbN/JX+nlKrIp2FDNuj8zg=
-----END RSA PRIVATE KEY-----

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -0,0 +1,38 @@
package controller
import (
"flswld.com/logger"
"github.com/gin-gonic/gin"
"io/ioutil"
"net/http"
)
func (c *Controller) headDataVersions(context *gin.Context) {
context.Header("Content-Type", "application/octet-stream")
context.Header("Content-Length", "514")
context.Status(http.StatusOK)
}
func (c *Controller) getDataVersions(context *gin.Context) {
dataVersions, err := ioutil.ReadFile("static/data_versions")
if err != nil {
logger.LOG.Error("open data_versions error")
return
}
context.Data(http.StatusOK, "application/octet-stream", dataVersions)
}
func (c *Controller) headBlk(context *gin.Context) {
context.Header("Content-Type", "application/octet-stream")
context.Header("Content-Length", "14103")
context.Status(http.StatusOK)
}
func (c *Controller) getBlk(context *gin.Context) {
blk, err := ioutil.ReadFile("static/29342328.blk")
if err != nil {
logger.LOG.Error("open 29342328.blk error")
return
}
context.Data(http.StatusOK, "application/octet-stream", blk)
}

View File

@@ -0,0 +1,147 @@
package controller
import (
"encoding/base64"
"flswld.com/common/config"
"flswld.com/light"
"flswld.com/logger"
"gate-hk4e/dao"
"gate-hk4e/region"
"github.com/gin-gonic/gin"
pb "google.golang.org/protobuf/proto"
"net/http"
"strconv"
)
type Controller struct {
dao *dao.Dao
rpcUserConsumer *light.Consumer
regionListBase64 string
regionCurrBase64 string
signRsaKey []byte
encRsaKeyMap map[string][]byte
pwdRsaKey []byte
}
func NewController(dao *dao.Dao, rpcUserConsumer *light.Consumer) (r *Controller) {
r = new(Controller)
r.dao = dao
r.rpcUserConsumer = rpcUserConsumer
r.regionListBase64 = ""
r.regionCurrBase64 = ""
regionCurr, regionList := region.InitRegion(config.CONF.Hk4e.KcpAddr, config.CONF.Hk4e.KcpPort)
r.signRsaKey, r.encRsaKeyMap, r.pwdRsaKey = region.LoadRsaKey()
regionCurrModify, err := pb.Marshal(regionCurr)
if err != nil {
logger.LOG.Error("Marshal QueryCurrRegionHttpRsp error")
return nil
}
r.regionCurrBase64 = base64.StdEncoding.EncodeToString(regionCurrModify)
regionListModify, err := pb.Marshal(regionList)
if err != nil {
logger.LOG.Error("Marshal QueryRegionListHttpRsp error")
return nil
}
r.regionListBase64 = base64.StdEncoding.EncodeToString(regionListModify)
r.runEngine()
return r
}
func (c *Controller) runEngine() {
if config.CONF.Logger.Level == "DEBUG" {
gin.SetMode(gin.DebugMode)
} else {
gin.SetMode(gin.ReleaseMode)
}
go func() {
engine := c.registerRouter()
port := config.CONF.HttpPort
addr := ":" + strconv.FormatInt(int64(port), 10)
err := engine.Run(addr)
if err != nil {
logger.LOG.Error("gin run error: %v", err)
}
}()
}
func (c *Controller) registerRouter() *gin.Engine {
engine := gin.Default()
{
// 404
engine.NoRoute(func(context *gin.Context) {
logger.LOG.Info("no route find, fallback to fuck mhy, url: %v", context.Request.RequestURI)
context.Header("Content-type", "text/html; charset=UTF-8")
context.Status(http.StatusNotFound)
_, _ = context.Writer.WriteString("FUCK MHY")
})
}
{
// 调度
// dispatchosglobal.yuanshen.com
engine.GET("/query_security_file", c.query_security_file)
engine.GET("/query_region_list", c.query_region_list)
// osusadispatch.yuanshen.com
engine.GET("/query_cur_region", c.query_cur_region)
}
{
// 登录
// hk4e-sdk-os.hoyoverse.com
// 账号登录
engine.POST("/hk4e_global/mdk/shield/api/login", c.apiLogin)
// token登录
engine.POST("/hk4e_global/mdk/shield/api/verify", c.apiVerify)
// 获取combo token
engine.POST("/hk4e_global/combo/granter/login/v2/login", c.v2Login)
}
{
// BLK文件补丁下载
// autopatchhk.yuanshen.com
engine.HEAD("/client_design_data/2.6_live/output_6988297_84eeb1c18b/client_silence/General/AssetBundles/data_versions", c.headDataVersions)
engine.GET("/client_design_data/2.6_live/output_6988297_84eeb1c18b/client_silence/General/AssetBundles/data_versions", c.getDataVersions)
engine.HEAD("/client_design_data/2.6_live/output_6988297_84eeb1c18b/client_silence/General/AssetBundles/blocks/00/29342328.blk", c.headBlk)
engine.GET("/client_design_data/2.6_live/output_6988297_84eeb1c18b/client_silence/General/AssetBundles/blocks/00/29342328.blk", c.getBlk)
}
{
// 日志
engine.POST("/sdk/dataUpload", c.sdkDataUpload)
engine.GET("/perf/config/verify", c.perfConfigVerify)
engine.POST("/perf/dataUpload", c.perfDataUpload)
engine.POST("/log", c.log8888)
engine.POST("/crash/dataUpload", c.crashDataUpload)
}
{
// 返回固定数据
// Windows
engine.GET("/hk4e_global/mdk/agreement/api/getAgreementInfos", c.getAgreementInfos)
engine.POST("/hk4e_global/combo/granter/api/compareProtocolVersion", c.postCompareProtocolVersion)
engine.POST("/account/risky/api/check", c.check)
engine.GET("/combo/box/api/config/sdk/combo", c.combo)
engine.GET("/hk4e_global/combo/granter/api/getConfig", c.getConfig)
engine.GET("/hk4e_global/mdk/shield/api/loadConfig", c.loadConfig)
engine.POST("/data_abtest_api/config/experiment/list", c.list)
engine.GET("/admin/mi18n/plat_oversea/m2020030410/m2020030410-version.json", c.version10Json)
engine.GET("/admin/mi18n/plat_oversea/m2020030410/m2020030410-zh-cn.json", c.zhCN10Json)
engine.GET("/geetestV2.html", c.geetestV2)
// Android
engine.POST("/common/h5log/log/batch", c.batch)
engine.GET("/hk4e_global/combo/granter/api/getFont", c.getFont)
engine.GET("/admin/mi18n/plat_oversea/m202003049/m202003049-version.json", c.version9Json)
engine.GET("/admin/mi18n/plat_oversea/m202003049/m202003049-zh-cn.json", c.zhCN9Json)
engine.GET("/hk4e_global/combo/granter/api/compareProtocolVersion", c.getCompareProtocolVersion)
// Android geetest
engine.GET("/gettype.php", c.gettype)
engine.GET("/get.php", c.get)
engine.POST("/ajax.php", c.ajax)
engine.GET("/ajax.php", c.ajax)
engine.GET("/static/appweb/app3-index.html", c.app3Index)
engine.GET("/static/js/slide.7.8.6.js", c.slideJs)
engine.GET("/favicon.ico", c.faviconIco)
engine.GET("/static/js/gct.e7810b5b525994e2fb1f89135f8df14a.js", c.js)
engine.GET("/static/ant/style_https.1.2.6.css", c.css)
engine.GET("/pictures/gt/a330cf996/a330cf996.webp", c.webp)
engine.GET("/pictures/gt/a330cf996/bg/86f9db021.webp", c.bgWebp)
engine.GET("/pictures/gt/a330cf996/slice/86f9db021.png", c.slicePng)
engine.GET("/static/ant/sprite2x.1.2.6.png", c.sprite2xPng)
}
return engine
}

View File

@@ -0,0 +1,144 @@
package controller
import (
"bytes"
"encoding/base64"
"flswld.com/common/utils/endec"
"flswld.com/logger"
"gate-hk4e/entity/api"
"github.com/gin-gonic/gin"
"io/ioutil"
"math"
"net/http"
"regexp"
"strconv"
)
func (c *Controller) query_security_file(context *gin.Context) {
file, err := ioutil.ReadFile("static/security_file")
if err != nil {
logger.LOG.Error("open security_file error")
return
}
context.Header("Content-type", "text/html; charset=UTF-8")
_, _ = context.Writer.WriteString(string(file))
}
func (c *Controller) query_region_list(context *gin.Context) {
context.Header("Content-type", "text/html; charset=UTF-8")
_, _ = context.Writer.WriteString(c.regionListBase64)
}
func (c *Controller) query_cur_region(context *gin.Context) {
versionName := context.Query("version")
response := "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="
if len(context.Request.URL.RawQuery) > 0 {
response = c.regionCurrBase64
}
reg, err := regexp.Compile("[0-9]+")
if err != nil {
logger.LOG.Error("compile regexp error: %v", err)
return
}
versionSlice := reg.FindAllString(versionName, -1)
version := 0
for index := 0; index < len(versionSlice); index++ {
v, err := strconv.Atoi(versionSlice[index])
if err != nil {
logger.LOG.Error("parse client version error: %v", err)
return
}
for i := 0; i < len(versionSlice)-1-index; i++ {
v *= 10
}
version += v
}
if version >= 1000 {
// 测试版本
version /= 10
}
if version >= 275 {
logger.LOG.Debug("do hk4e 2.8 rsa logic")
if context.Query("dispatchSeed") == "" {
rsp := &api.QueryCurRegionRspJson{
Content: response,
Sign: "TW9yZSBsb3ZlIGZvciBVQSBQYXRjaCBwbGF5ZXJz",
}
context.JSON(http.StatusOK, rsp)
return
}
keyId := context.Query("key_id")
encPubPrivKey, exist := c.encRsaKeyMap[keyId]
if !exist {
logger.LOG.Error("can not found key id: %v", keyId)
return
}
regionInfo, err := base64.StdEncoding.DecodeString(response)
if err != nil {
logger.LOG.Error("decode region info error: %v", err)
return
}
chunkSize := 256 - 11
regionInfoLength := len(regionInfo)
numChunks := int(math.Ceil(float64(regionInfoLength) / float64(chunkSize)))
encryptedRegionInfo := make([]byte, 0)
for i := 0; i < numChunks; i++ {
from := i * chunkSize
to := int(math.Min(float64((i+1)*chunkSize), float64(regionInfoLength)))
chunk := regionInfo[from:to]
pubKey, err := endec.RsaParsePubKeyByPrivKey(encPubPrivKey)
if err != nil {
logger.LOG.Error("parse rsa pub key error: %v", err)
return
}
privKey, err := endec.RsaParsePrivKey(encPubPrivKey)
if err != nil {
logger.LOG.Error("parse rsa priv key error: %v", err)
return
}
encrypt, err := endec.RsaEncrypt(chunk, pubKey)
if err != nil {
logger.LOG.Error("rsa enc error: %v", err)
return
}
decrypt, err := endec.RsaDecrypt(encrypt, privKey)
if err != nil {
logger.LOG.Error("rsa dec error: %v", err)
return
}
if bytes.Compare(decrypt, chunk) != 0 {
logger.LOG.Error("rsa dec test fail")
return
}
encryptedRegionInfo = append(encryptedRegionInfo, encrypt...)
}
signPrivkey, err := endec.RsaParsePrivKey(c.signRsaKey)
if err != nil {
logger.LOG.Error("parse rsa priv key error: %v", err)
return
}
signData, err := endec.RsaSign(regionInfo, signPrivkey)
if err != nil {
logger.LOG.Error("rsa sign error: %v", err)
return
}
ok, err := endec.RsaVerify(regionInfo, signData, &signPrivkey.PublicKey)
if err != nil {
logger.LOG.Error("rsa verify error: %v", err)
return
}
if !ok {
logger.LOG.Error("rsa verify test fail")
return
}
rsp := &api.QueryCurRegionRspJson{
Content: base64.StdEncoding.EncodeToString(encryptedRegionInfo),
Sign: base64.StdEncoding.EncodeToString(signData),
}
context.JSON(http.StatusOK, rsp)
return
} else {
context.Header("Content-type", "text/html; charset=UTF-8")
_, _ = context.Writer.WriteString(response)
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,33 @@
package controller
import "github.com/gin-gonic/gin"
// POST https://log-upload-os.mihoyo.com/sdk/dataUpload HTTP/1.1
func (c *Controller) sdkDataUpload(context *gin.Context) {
context.Header("Content-type", "application/json")
_, _ = context.Writer.WriteString("{\"code\":0}")
}
// GET http://log-upload-os.hoyoverse.com/perf/config/verify?device_id=dd664c97f924af747b4576a297c132038be239291651474673768&platform=2&name=DESKTOP-EDUS2DL HTTP/1.1
func (c *Controller) perfConfigVerify(context *gin.Context) {
context.Header("Content-type", "application/json")
_, _ = context.Writer.WriteString("{\"code\":0}")
}
// POST http://log-upload-os.hoyoverse.com/perf/dataUpload HTTP/1.1
func (c *Controller) perfDataUpload(context *gin.Context) {
context.Header("Content-type", "application/json")
_, _ = context.Writer.WriteString("{\"code\":0}")
}
// POST http://overseauspider.yuanshen.com:8888/log HTTP/1.1
func (c *Controller) log8888(context *gin.Context) {
context.Header("Content-type", "application/json")
_, _ = context.Writer.WriteString("{\"code\":0}")
}
// POST http://log-upload-os.hoyoverse.com/crash/dataUpload HTTP/1.1
func (c *Controller) crashDataUpload(context *gin.Context) {
context.Header("Content-type", "application/json")
_, _ = context.Writer.WriteString("{\"code\":0}")
}

View File

@@ -0,0 +1,249 @@
package controller
import (
"crypto/md5"
"encoding/base64"
"encoding/hex"
"encoding/json"
providerApiEntity "flswld.com/annie-user-api/entity"
"flswld.com/common/utils/endec"
"flswld.com/common/utils/random"
"flswld.com/logger"
"gate-hk4e/entity/api"
"gate-hk4e/entity/db"
"github.com/gin-gonic/gin"
"net/http"
"regexp"
"strconv"
"strings"
)
func (c *Controller) getMD5(src string) string {
ctx := md5.New()
ctx.Write([]byte(src))
return hex.EncodeToString(ctx.Sum(nil))
}
func (c *Controller) apiLogin(context *gin.Context) {
requestData := new(api.LoginAccountRequestJson)
err := context.ShouldBindJSON(requestData)
if err != nil {
logger.LOG.Error("parse LoginAccountRequestJson error: %v", err)
return
}
encPwdData, err := base64.StdEncoding.DecodeString(requestData.Password)
if err != nil {
logger.LOG.Error("decode password enc data error: %v", err)
return
}
pwdPrivKey, err := endec.RsaParsePrivKey(c.pwdRsaKey)
if err != nil {
logger.LOG.Error("parse rsa key error: %v", err)
return
}
pwdDecData, err := endec.RsaDecrypt(encPwdData, pwdPrivKey)
useAtAtMode := false
if err != nil {
logger.LOG.Debug("rsa dec error: %v", err)
logger.LOG.Debug("password rsa dec fail, fallback to @@ mode")
useAtAtMode = true
} else {
logger.LOG.Debug("password dec: %v", string(pwdDecData))
useAtAtMode = false
}
responseData := api.NewLoginResult()
var username string
var password string
if useAtAtMode {
// 账号格式检查 用户名6-20字符 密码8-20字符 用户名和密码公用account字段 第一次出现的@@视为分割标识 username@@password
if len(requestData.Account) > 20+20+2 {
responseData.Retcode = -201
responseData.Message = "用户名或密码长度超限"
context.JSON(http.StatusOK, responseData)
return
}
if !strings.Contains(requestData.Account, "@@") {
responseData.Retcode = -201
responseData.Message = "用户名同密码均填写到用户名输入框,填写格式为:用户名@@密码,密码输入框填写任意字符均可"
context.JSON(http.StatusOK, responseData)
return
}
atIndex := strings.Index(requestData.Account, "@@")
username = requestData.Account[:atIndex]
password = requestData.Account[atIndex+2:]
} else {
username = requestData.Account
password = string(pwdDecData)
}
if len(username) < 6 || len(username) > 20 {
responseData.Retcode = -201
responseData.Message = "用户名为6-20位字符"
context.JSON(http.StatusOK, responseData)
return
}
if len(password) < 8 || len(password) > 20 {
responseData.Retcode = -201
responseData.Message = "密码为8-20位字符"
context.JSON(http.StatusOK, responseData)
return
}
ok, err := regexp.MatchString("^[a-zA-Z0-9]{6,20}$", username)
if err != nil || !ok {
responseData.Retcode = -201
responseData.Message = "用户名只能包含大小写字母和数字"
context.JSON(http.StatusOK, responseData)
return
}
userList := make([]providerApiEntity.User, 0)
ok = c.rpcUserConsumer.CallFunction("RpcService", "RpcQueryUser", &providerApiEntity.User{Username: username}, &userList)
if !ok {
responseData.Retcode = -201
responseData.Message = "服务器内部错误:-1"
context.JSON(http.StatusOK, responseData)
return
}
if len(userList) != 1 {
responseData.Retcode = -201
responseData.Message = "用户名不存在"
context.JSON(http.StatusOK, responseData)
return
}
user := userList[0]
if c.getMD5(password) != user.Password {
responseData.Retcode = -201
responseData.Message = "用户名或密码错误"
context.JSON(http.StatusOK, responseData)
return
}
// 登录成功
account, err := c.dao.QueryAccountByField("username", username)
if err != nil {
logger.LOG.Error("query account from db error: %v", err)
return
}
if account == nil {
// 注册一个原神account
playerID, err := c.dao.GetNextYuanShenUid()
if err != nil {
responseData.Retcode = -201
responseData.Message = "服务器内部错误:-2"
context.JSON(http.StatusOK, responseData)
return
}
regAccount := &db.Account{
Uid: user.Uid,
Username: username,
PlayerID: playerID,
Token: base64.StdEncoding.EncodeToString(random.GetRandomByte(24)),
ComboToken: "",
}
_, err = c.dao.InsertAccount(regAccount)
if err != nil {
responseData.Retcode = -201
responseData.Message = "服务器内部错误:-3"
context.JSON(http.StatusOK, responseData)
return
}
responseData.Message = "OK"
responseData.Data.Account.Uid = strconv.FormatInt(int64(regAccount.Uid), 10)
responseData.Data.Account.Token = regAccount.Token
responseData.Data.Account.Email = regAccount.Username
} else {
// 生产新的token
account.Token = base64.StdEncoding.EncodeToString(random.GetRandomByte(24))
_, err := c.dao.UpdateAccountFieldByFieldName("uid", account.Uid, "token", account.Token)
if err != nil {
responseData.Retcode = -201
responseData.Message = "服务器内部错误:-4"
context.JSON(http.StatusOK, responseData)
return
}
responseData.Message = "OK"
responseData.Data.Account.Uid = strconv.FormatInt(int64(account.Uid), 10)
responseData.Data.Account.Token = account.Token
responseData.Data.Account.Email = account.Username
}
context.JSON(http.StatusOK, responseData)
}
func (c *Controller) apiVerify(context *gin.Context) {
requestData := new(api.LoginTokenRequest)
err := context.ShouldBindJSON(requestData)
if err != nil {
logger.LOG.Error("parse LoginTokenRequest error: %v", err)
return
}
uid, err := strconv.ParseInt(requestData.Uid, 10, 64)
if err != nil {
logger.LOG.Error("parse uid error: %v", err)
return
}
account, err := c.dao.QueryAccountByField("uid", uid)
if err != nil {
logger.LOG.Error("query account from db error: %v", err)
return
}
responseData := api.NewLoginResult()
if account == nil || account.Token != requestData.Token {
responseData.Retcode = -111
responseData.Message = "账号本地缓存信息错误"
context.JSON(http.StatusOK, responseData)
return
}
responseData.Message = "OK"
responseData.Data.Account.Uid = requestData.Uid
responseData.Data.Account.Token = requestData.Token
responseData.Data.Account.Email = account.Username
context.JSON(http.StatusOK, responseData)
}
func (c *Controller) v2Login(context *gin.Context) {
requestData := new(api.ComboTokenReq)
err := context.ShouldBindJSON(requestData)
if err != nil {
logger.LOG.Error("parse ComboTokenReq error: %v", err)
return
}
data := requestData.Data
if len(data) == 0 {
logger.LOG.Error("requestData.Data len == 0")
return
}
loginData := new(api.LoginTokenData)
err = json.Unmarshal([]byte(data), loginData)
if err != nil {
logger.LOG.Error("Unmarshal LoginTokenData error: %v", err)
return
}
uid, err := strconv.ParseInt(loginData.Uid, 10, 64)
if err != nil {
logger.LOG.Error("ParseInt uid error: %v", err)
return
}
responseData := api.NewComboTokenRes()
account, err := c.dao.QueryAccountByField("uid", uid)
if account == nil || account.Token != loginData.Token {
responseData.Retcode = -201
responseData.Message = "token错误"
context.JSON(http.StatusOK, responseData)
return
}
// 生成新的comboToken
account.ComboToken = random.GetRandomByteHexStr(20)
_, err = c.dao.UpdateAccountFieldByFieldName("uid", account.Uid, "comboToken", account.ComboToken)
if err != nil {
responseData.Retcode = -201
responseData.Message = "服务器内部错误:-1"
context.JSON(http.StatusOK, responseData)
return
}
responseData.Message = "OK"
responseData.Data.OpenID = loginData.Uid
responseData.Data.ComboID = "0"
responseData.Data.ComboToken = account.ComboToken
context.JSON(http.StatusOK, responseData)
}

View File

@@ -0,0 +1,126 @@
package dao
import (
"context"
"flswld.com/logger"
dbEntity "gate-hk4e/entity/db"
"github.com/pkg/errors"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
func (d *Dao) GetNextYuanShenUid() (uint64, error) {
db := d.db.Collection("player_id_counter")
find := db.FindOne(context.TODO(), bson.D{{"_id", "default"}})
item := new(dbEntity.PlayerIDCounter)
err := find.Decode(item)
if err != nil {
if err == mongo.ErrNoDocuments {
item := &dbEntity.PlayerIDCounter{
ID: "default",
PlayerID: 100000001,
}
_, err := db.InsertOne(context.TODO(), item)
if err != nil {
return 0, errors.New("insert new PlayerID error")
}
return item.PlayerID, nil
} else {
return 0, err
}
}
item.PlayerID++
_, err = db.UpdateOne(
context.TODO(),
bson.D{
{"_id", "default"},
},
bson.D{
{"$set", bson.D{
{"PlayerID", item.PlayerID},
}},
},
)
if err != nil {
return 0, err
}
return item.PlayerID, nil
}
func (d *Dao) InsertAccount(account *dbEntity.Account) (primitive.ObjectID, error) {
db := d.db.Collection("account")
id, err := db.InsertOne(context.TODO(), account)
if err != nil {
return primitive.ObjectID{}, err
} else {
_id, ok := id.InsertedID.(primitive.ObjectID)
if !ok {
logger.LOG.Error("get insert id error")
return primitive.ObjectID{}, nil
}
return _id, nil
}
}
func (d *Dao) DeleteAccountByUsername(username string) (int64, error) {
db := d.db.Collection("account")
deleteCount, err := db.DeleteOne(
context.TODO(),
bson.D{
{"username", username},
},
)
if err != nil {
return 0, err
} else {
return deleteCount.DeletedCount, nil
}
}
func (d *Dao) UpdateAccountFieldByFieldName(fieldName string, fieldValue any, fieldUpdateName string, fieldUpdateValue any) (int64, error) {
db := d.db.Collection("account")
updateCount, err := db.UpdateOne(
context.TODO(),
bson.D{
{fieldName, fieldValue},
},
bson.D{
{"$set", bson.D{
{fieldUpdateName, fieldUpdateValue},
}},
},
)
if err != nil {
return 0, err
} else {
return updateCount.ModifiedCount, nil
}
}
func (d *Dao) QueryAccountByField(fieldName string, fieldValue any) (*dbEntity.Account, error) {
db := d.db.Collection("account")
find, err := db.Find(
context.TODO(),
bson.D{
{fieldName, fieldValue},
},
)
if err != nil {
return nil, err
}
result := make([]*dbEntity.Account, 0)
for find.Next(context.TODO()) {
item := new(dbEntity.Account)
err := find.Decode(item)
if err != nil {
return nil, err
}
result = append(result, item)
}
if len(result) == 0 {
return nil, nil
} else {
return result[0], nil
}
}

34
gate-hk4e/dao/dao.go Normal file
View File

@@ -0,0 +1,34 @@
package dao
import (
"context"
"flswld.com/common/config"
"flswld.com/logger"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
type Dao struct {
client *mongo.Client
db *mongo.Database
}
func NewDao() (r *Dao) {
r = new(Dao)
clientOptions := options.Client().ApplyURI(config.CONF.Database.Url)
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
logger.LOG.Error("mongo connect error: %v", err)
return nil
}
r.client = client
r.db = client.Database("gate_hk4e")
return r
}
func (d *Dao) CloseDao() {
err := d.client.Disconnect(context.TODO())
if err != nil {
logger.LOG.Error("mongo close error: %v", err)
}
}

View File

@@ -0,0 +1,15 @@
package api
type ComboTokenReq struct {
AppID int `json:"app_id"`
ChannelID int `json:"channel_id"`
Data string `json:"data"`
Device string `json:"device"`
Sign string `json:"sign"`
}
type LoginTokenData struct {
Uid string `json:"uid"`
Token string `json:"token"`
Guest bool `json:"guest"`
}

View File

@@ -0,0 +1,34 @@
package api
type ComboTokenRes struct {
Message string `json:"message"`
Retcode int `json:"retcode"`
Data LoginData `json:"data"`
}
type LoginData struct {
AccountType int `json:"account_type"`
Heartbeat bool `json:"heartbeat"`
ComboID string `json:"combo_id"`
ComboToken string `json:"combo_token"`
OpenID string `json:"open_id"`
Data string `json:"data"`
FatigueRemind any `json:"fatigue_remind"`
}
func NewComboTokenRes() (r *ComboTokenRes) {
r = &ComboTokenRes{
Message: "",
Retcode: 0,
Data: LoginData{
AccountType: 1,
Heartbeat: false,
ComboID: "",
ComboToken: "",
OpenID: "",
Data: "{\"guest\":false}",
FatigueRemind: nil,
},
}
return r
}

View File

@@ -0,0 +1,7 @@
package api
type LoginAccountRequestJson struct {
Account string `json:"account"`
Password string `json:"password"`
IsCrypto bool `json:"is_crypto"`
}

View File

@@ -0,0 +1,74 @@
package api
type LoginResult struct {
Message string `json:"message"`
Retcode int `json:"retcode"`
Data VerifyData `json:"data"`
}
type VerifyData struct {
Account VerifyAccountData `json:"account"`
DeviceGrantRequired bool `json:"device_grant_required"`
RealnameOperation string `json:"realname_operation"`
RealpersonRequired bool `json:"realperson_required"`
SafeMobileRequired bool `json:"safe_mobile_required"`
}
type VerifyAccountData struct {
Uid string `json:"uid"`
Name string `json:"name"`
Email string `json:"email"`
Mobile string `json:"mobile"`
IsEmailVerify string `json:"is_email_verify"`
Realname string `json:"realname"`
IdentityCard string `json:"identity_card"`
Token string `json:"token"`
SafeMobile string `json:"safe_mobile"`
FacebookName string `json:"facebook_name"`
TwitterName string `json:"twitter_name"`
GameCenterName string `json:"game_center_name"`
GoogleName string `json:"google_name"`
AppleName string `json:"apple_name"`
SonyName string `json:"sony_name"`
TapName string `json:"tap_name"`
Country string `json:"country"`
ReactivateTicket string `json:"reactivate_ticket"`
AreaCode string `json:"area_code"`
DeviceGrantTicket string `json:"device_grant_ticket"`
}
func NewLoginResult() (r *LoginResult) {
r = &LoginResult{
Message: "",
Retcode: 0,
Data: VerifyData{
Account: VerifyAccountData{
Uid: "",
Name: "",
Email: "",
Mobile: "",
IsEmailVerify: "0",
Realname: "",
IdentityCard: "",
Token: "",
SafeMobile: "",
FacebookName: "",
TwitterName: "",
GameCenterName: "",
GoogleName: "",
AppleName: "",
SonyName: "",
TapName: "",
Country: "CN",
ReactivateTicket: "",
AreaCode: "**",
DeviceGrantTicket: "",
},
DeviceGrantRequired: false,
RealnameOperation: "None",
RealpersonRequired: false,
SafeMobileRequired: false,
},
}
return r
}

View File

@@ -0,0 +1,6 @@
package api
type LoginTokenRequest struct {
Uid string `json:"uid"`
Token string `json:"token"`
}

View File

@@ -0,0 +1,6 @@
package api
type QueryCurRegionRspJson struct {
Content string `json:"content"`
Sign string `json:"sign"`
}

View File

@@ -0,0 +1,14 @@
package db
import "go.mongodb.org/mongo-driver/bson/primitive"
type Account struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Uid uint64 `bson:"uid"`
Username string `bson:"username"`
PlayerID uint64 `bson:"playerID"`
Token string `bson:"token"`
ComboToken string `bson:"comboToken"`
Forbid bool `bson:"forbid"`
ForbidEndTime uint64 `bson:"forbidEndTime"`
}

View File

@@ -0,0 +1,6 @@
package db
type PlayerIDCounter struct {
ID string `bson:"_id"`
PlayerID uint64 `bson:"PlayerID"`
}

View File

@@ -0,0 +1,619 @@
package forward
import (
"flswld.com/common/config"
"flswld.com/gate-hk4e-api/gm"
"flswld.com/gate-hk4e-api/proto"
"flswld.com/logger"
"gate-hk4e/dao"
"gate-hk4e/kcp"
"gate-hk4e/net"
"gate-hk4e/region"
"io/ioutil"
"runtime"
"sync"
"time"
)
const (
ConnWaitToken = iota
ConnWaitLogin
ConnAlive
ConnClose
)
type ClientHeadMeta struct {
seq uint32
}
type ForwardManager struct {
dao *dao.Dao
protoMsgInput chan *net.ProtoMsg
protoMsgOutput chan *net.ProtoMsg
netMsgInput chan *proto.NetMsg
netMsgOutput chan *proto.NetMsg
// 玩家登录相关
connStateMap map[uint64]uint8
connStateMapLock sync.RWMutex
// kcpConv -> userID
convUserIdMap map[uint64]uint32
convUserIdMapLock sync.RWMutex
// userID -> kcpConv
userIdConvMap map[uint32]uint64
userIdConvMapLock sync.RWMutex
// kcpConv -> ipAddr
convAddrMap map[uint64]string
convAddrMapLock sync.RWMutex
// kcpConv -> headMeta
convHeadMetaMap map[uint64]*ClientHeadMeta
convHeadMetaMapLock sync.RWMutex
secretKeyBuffer []byte
kcpEventInput chan *net.KcpEvent
kcpEventOutput chan *net.KcpEvent
regionCurr *proto.QueryCurrRegionHttpRsp
signRsaKey []byte
encRsaKeyMap map[string][]byte
}
func NewForwardManager(dao *dao.Dao,
protoMsgInput chan *net.ProtoMsg, protoMsgOutput chan *net.ProtoMsg,
kcpEventInput chan *net.KcpEvent, kcpEventOutput chan *net.KcpEvent,
netMsgInput chan *proto.NetMsg, netMsgOutput chan *proto.NetMsg) (r *ForwardManager) {
r = new(ForwardManager)
r.dao = dao
r.protoMsgInput = protoMsgInput
r.protoMsgOutput = protoMsgOutput
r.netMsgInput = netMsgInput
r.netMsgOutput = netMsgOutput
r.connStateMap = make(map[uint64]uint8)
r.convUserIdMap = make(map[uint64]uint32)
r.userIdConvMap = make(map[uint32]uint64)
r.convAddrMap = make(map[uint64]string)
r.convHeadMetaMap = make(map[uint64]*ClientHeadMeta)
r.kcpEventInput = kcpEventInput
r.kcpEventOutput = kcpEventOutput
return r
}
func (f *ForwardManager) getHeadMsg(clientSeq uint32) (headMsg *proto.PacketHead) {
headMsg = new(proto.PacketHead)
if clientSeq != 0 {
headMsg.ClientSequenceId = clientSeq
headMsg.SentMs = uint64(time.Now().UnixMilli())
}
return headMsg
}
func (f *ForwardManager) kcpEventHandle() {
for {
event := <-f.kcpEventOutput
logger.LOG.Info("rpc manager recv event, ConvId: %v, EventId: %v", event.ConvId, event.EventId)
switch event.EventId {
case net.KcpPacketSendNotify:
// 发包通知
// 关闭发包监听
f.kcpEventInput <- &net.KcpEvent{
ConvId: event.ConvId,
EventId: net.KcpPacketSendListen,
EventMessage: "Disable",
}
// 登录成功 通知GS初始化相关数据
userId, exist := f.getUserIdByConvId(event.ConvId)
if !exist {
logger.LOG.Error("can not find userId by convId")
continue
}
headMeta, exist := f.getHeadMetaByConvId(event.ConvId)
if !exist {
logger.LOG.Error("can not find client head metadata by convId")
continue
}
netMsg := new(proto.NetMsg)
netMsg.UserId = userId
netMsg.EventId = proto.UserLoginNotify
netMsg.ClientSeq = headMeta.seq
f.netMsgInput <- netMsg
logger.LOG.Info("send to gs user login ok, ConvId: %v, UserId: %v", event.ConvId, netMsg.UserId)
case net.KcpConnCloseNotify:
// 连接断开通知
userId, exist := f.getUserIdByConvId(event.ConvId)
if !exist {
logger.LOG.Error("can not find userId by convId")
continue
}
if f.getConnState(event.ConvId) == ConnAlive {
// 通知GS玩家下线
netMsg := new(proto.NetMsg)
netMsg.UserId = userId
netMsg.EventId = proto.UserOfflineNotify
f.netMsgInput <- netMsg
logger.LOG.Info("send to gs user offline, ConvId: %v, UserId: %v", event.ConvId, netMsg.UserId)
}
// 删除各种map数据
f.deleteConnState(event.ConvId)
f.deleteUserIdByConvId(event.ConvId)
currConvId, currExist := f.getConvIdByUserId(userId)
if currExist && currConvId == event.ConvId {
// 防止误删顶号的新连接数据
f.deleteConvIdByUserId(userId)
}
f.deleteAddrByConvId(event.ConvId)
f.deleteHeadMetaByConvId(event.ConvId)
case net.KcpConnEstNotify:
// 连接建立通知
addr, ok := event.EventMessage.(string)
if !ok {
logger.LOG.Error("event KcpConnEstNotify msg type error")
continue
}
f.setAddrByConvId(event.ConvId, addr)
case net.KcpConnRttNotify:
// 客户端往返时延通知
rtt, ok := event.EventMessage.(int32)
if !ok {
logger.LOG.Error("event KcpConnRttNotify msg type error")
continue
}
// 通知GS玩家客户端往返时延
userId, exist := f.getUserIdByConvId(event.ConvId)
if !exist {
logger.LOG.Error("can not find userId by convId")
continue
}
netMsg := new(proto.NetMsg)
netMsg.UserId = userId
netMsg.EventId = proto.ClientRttNotify
netMsg.ClientRtt = uint32(rtt)
f.netMsgInput <- netMsg
case net.KcpConnAddrChangeNotify:
// 客户端网络地址改变通知
f.convAddrMapLock.Lock()
_, exist := f.convAddrMap[event.ConvId]
if !exist {
f.convAddrMapLock.Unlock()
logger.LOG.Error("conn addr change but conn can not be found")
continue
}
addr := event.EventMessage.(string)
f.convAddrMap[event.ConvId] = addr
f.convAddrMapLock.Unlock()
}
}
}
func (f *ForwardManager) Start() {
// 读取密钥相关文件
var err error = nil
f.secretKeyBuffer, err = ioutil.ReadFile("static/secretKeyBuffer.bin")
if err != nil {
logger.LOG.Error("open secretKeyBuffer.bin error")
return
}
f.signRsaKey, f.encRsaKeyMap, _ = region.LoadRsaKey()
// region
regionCurr, _ := region.InitRegion(config.CONF.Hk4e.KcpAddr, config.CONF.Hk4e.KcpPort)
f.regionCurr = regionCurr
// kcp事件监听
go f.kcpEventHandle()
go f.recvNetMsgFromGameServer()
// 接收客户端消息
cpuCoreNum := runtime.NumCPU()
for i := 0; i < cpuCoreNum*10; i++ {
go f.sendNetMsgToGameServer()
}
}
// 发送消息到GS
func (f *ForwardManager) sendNetMsgToGameServer() {
for {
protoMsg := <-f.protoMsgOutput
if protoMsg.HeadMessage == nil {
logger.LOG.Error("recv null head msg: %v", protoMsg)
}
f.setHeadMetaByConvId(protoMsg.ConvId, &ClientHeadMeta{
seq: protoMsg.HeadMessage.ClientSequenceId,
})
connState := f.getConnState(protoMsg.ConvId)
// gate本地处理的请求
switch protoMsg.ApiId {
case proto.ApiGetPlayerTokenReq:
// 获取玩家token请求
if connState != ConnWaitToken {
continue
}
getPlayerTokenReq := protoMsg.PayloadMessage.(*proto.GetPlayerTokenReq)
getPlayerTokenRsp := f.getPlayerToken(protoMsg.ConvId, getPlayerTokenReq)
if getPlayerTokenRsp == nil {
continue
}
// 改变解密密钥
f.kcpEventInput <- &net.KcpEvent{
ConvId: protoMsg.ConvId,
EventId: net.KcpXorKeyChange,
EventMessage: "DEC",
}
// 返回数据到客户端
resp := new(net.ProtoMsg)
resp.ConvId = protoMsg.ConvId
resp.ApiId = proto.ApiGetPlayerTokenRsp
resp.HeadMessage = f.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
resp.PayloadMessage = getPlayerTokenRsp
f.protoMsgInput <- resp
case proto.ApiPlayerLoginReq:
// 玩家登录请求
if connState != ConnWaitLogin {
continue
}
playerLoginReq := protoMsg.PayloadMessage.(*proto.PlayerLoginReq)
playerLoginRsp := f.playerLogin(protoMsg.ConvId, playerLoginReq)
if playerLoginRsp == nil {
continue
}
// 改变加密密钥
f.kcpEventInput <- &net.KcpEvent{
ConvId: protoMsg.ConvId,
EventId: net.KcpXorKeyChange,
EventMessage: "ENC",
}
// 开启发包监听
f.kcpEventInput <- &net.KcpEvent{
ConvId: protoMsg.ConvId,
EventId: net.KcpPacketSendListen,
EventMessage: "Enable",
}
go func() {
// 保证kcp事件已成功生效
time.Sleep(time.Millisecond * 50)
// 返回数据到客户端
resp := new(net.ProtoMsg)
resp.ConvId = protoMsg.ConvId
resp.ApiId = proto.ApiPlayerLoginRsp
resp.HeadMessage = f.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
resp.PayloadMessage = playerLoginRsp
f.protoMsgInput <- resp
}()
case proto.ApiSetPlayerBornDataReq:
// 玩家注册请求
if connState != ConnAlive {
continue
}
userId, exist := f.getUserIdByConvId(protoMsg.ConvId)
if !exist {
logger.LOG.Error("can not find userId by convId")
continue
}
netMsg := new(proto.NetMsg)
netMsg.UserId = userId
netMsg.EventId = proto.UserRegNotify
netMsg.ApiId = proto.ApiSetPlayerBornDataReq
netMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId
netMsg.PayloadMessage = protoMsg.PayloadMessage
f.netMsgInput <- netMsg
case proto.ApiPlayerForceExitRsp:
// 玩家退出游戏请求
if connState != ConnAlive {
continue
}
userId, exist := f.getUserIdByConvId(protoMsg.ConvId)
if !exist {
logger.LOG.Error("can not find userId by convId")
continue
}
f.setConnState(protoMsg.ConvId, ConnClose)
info := new(gm.KickPlayerInfo)
info.UserId = userId
info.Reason = uint32(kcp.EnetServerKick)
f.KickPlayer(info)
case proto.ApiPingReq:
// ping请求
if connState != ConnAlive {
continue
}
pingReq := protoMsg.PayloadMessage.(*proto.PingReq)
logger.LOG.Debug("user ping req, data: %v", pingReq.String())
// 返回数据到客户端
// TODO 记录客户端最后一次ping时间做超时下线处理
pingRsp := new(proto.PingRsp)
pingRsp.ClientTime = pingReq.ClientTime
resp := new(net.ProtoMsg)
resp.ConvId = protoMsg.ConvId
resp.ApiId = proto.ApiPingRsp
resp.HeadMessage = f.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
resp.PayloadMessage = pingRsp
f.protoMsgInput <- resp
// 通知GS玩家客户端的本地时钟
userId, exist := f.getUserIdByConvId(protoMsg.ConvId)
if !exist {
logger.LOG.Error("can not find userId by convId")
continue
}
netMsg := new(proto.NetMsg)
netMsg.UserId = userId
netMsg.EventId = proto.ClientTimeNotify
netMsg.ClientTime = pingReq.ClientTime
f.netMsgInput <- netMsg
default:
// 转发到GS
// 未登录禁止访问GS
if connState != ConnAlive {
continue
}
netMsg := new(proto.NetMsg)
userId, exist := f.getUserIdByConvId(protoMsg.ConvId)
if exist {
netMsg.UserId = userId
} else {
logger.LOG.Error("can not find userId by convId")
continue
}
netMsg.EventId = proto.NormalMsg
netMsg.ApiId = protoMsg.ApiId
netMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId
netMsg.PayloadMessage = protoMsg.PayloadMessage
f.netMsgInput <- netMsg
}
}
}
// 从GS接收消息
func (f *ForwardManager) recvNetMsgFromGameServer() {
for {
netMsg := <-f.netMsgOutput
convId, exist := f.getConvIdByUserId(netMsg.UserId)
if !exist {
logger.LOG.Error("can not find convId by userId")
continue
}
if netMsg.EventId == proto.NormalMsg {
protoMsg := new(net.ProtoMsg)
protoMsg.ConvId = convId
protoMsg.ApiId = netMsg.ApiId
protoMsg.HeadMessage = f.getHeadMsg(netMsg.ClientSeq)
protoMsg.PayloadMessage = netMsg.PayloadMessage
f.protoMsgInput <- protoMsg
continue
} else {
logger.LOG.Error("recv unknown event from game server, event id: %v", netMsg.EventId)
continue
}
}
}
func (f *ForwardManager) getConnState(convId uint64) uint8 {
f.connStateMapLock.RLock()
connState, connStateExist := f.connStateMap[convId]
f.connStateMapLock.RUnlock()
if !connStateExist {
connState = ConnWaitToken
f.connStateMapLock.Lock()
f.connStateMap[convId] = ConnWaitToken
f.connStateMapLock.Unlock()
}
return connState
}
func (f *ForwardManager) setConnState(convId uint64, state uint8) {
f.connStateMapLock.Lock()
f.connStateMap[convId] = state
f.connStateMapLock.Unlock()
}
func (f *ForwardManager) deleteConnState(convId uint64) {
f.connStateMapLock.Lock()
delete(f.connStateMap, convId)
f.connStateMapLock.Unlock()
}
func (f *ForwardManager) getUserIdByConvId(convId uint64) (userId uint32, exist bool) {
f.convUserIdMapLock.RLock()
userId, exist = f.convUserIdMap[convId]
f.convUserIdMapLock.RUnlock()
return userId, exist
}
func (f *ForwardManager) setUserIdByConvId(convId uint64, userId uint32) {
f.convUserIdMapLock.Lock()
f.convUserIdMap[convId] = userId
f.convUserIdMapLock.Unlock()
}
func (f *ForwardManager) deleteUserIdByConvId(convId uint64) {
f.convUserIdMapLock.Lock()
delete(f.convUserIdMap, convId)
f.convUserIdMapLock.Unlock()
}
func (f *ForwardManager) getConvIdByUserId(userId uint32) (convId uint64, exist bool) {
f.userIdConvMapLock.RLock()
convId, exist = f.userIdConvMap[userId]
f.userIdConvMapLock.RUnlock()
return convId, exist
}
func (f *ForwardManager) setConvIdByUserId(userId uint32, convId uint64) {
f.userIdConvMapLock.Lock()
f.userIdConvMap[userId] = convId
f.userIdConvMapLock.Unlock()
}
func (f *ForwardManager) deleteConvIdByUserId(userId uint32) {
f.userIdConvMapLock.Lock()
delete(f.userIdConvMap, userId)
f.userIdConvMapLock.Unlock()
}
func (f *ForwardManager) getAddrByConvId(convId uint64) (addr string, exist bool) {
f.convAddrMapLock.RLock()
addr, exist = f.convAddrMap[convId]
f.convAddrMapLock.RUnlock()
return addr, exist
}
func (f *ForwardManager) setAddrByConvId(convId uint64, addr string) {
f.convAddrMapLock.Lock()
f.convAddrMap[convId] = addr
f.convAddrMapLock.Unlock()
}
func (f *ForwardManager) deleteAddrByConvId(convId uint64) {
f.convAddrMapLock.Lock()
delete(f.convAddrMap, convId)
f.convAddrMapLock.Unlock()
}
func (f *ForwardManager) getHeadMetaByConvId(convId uint64) (headMeta *ClientHeadMeta, exist bool) {
f.convHeadMetaMapLock.RLock()
headMeta, exist = f.convHeadMetaMap[convId]
f.convHeadMetaMapLock.RUnlock()
return headMeta, exist
}
func (f *ForwardManager) setHeadMetaByConvId(convId uint64, headMeta *ClientHeadMeta) {
f.convHeadMetaMapLock.Lock()
f.convHeadMetaMap[convId] = headMeta
f.convHeadMetaMapLock.Unlock()
}
func (f *ForwardManager) deleteHeadMetaByConvId(convId uint64) {
f.convHeadMetaMapLock.Lock()
delete(f.convHeadMetaMap, convId)
f.convHeadMetaMapLock.Unlock()
}
// 改变网关开放状态
func (f *ForwardManager) ChangeGateOpenState(isOpen bool) bool {
f.kcpEventInput <- &net.KcpEvent{
EventId: net.KcpGateOpenState,
EventMessage: isOpen,
}
logger.LOG.Info("change gate open state to: %v", isOpen)
return true
}
// 剔除玩家下线
func (f *ForwardManager) KickPlayer(info *gm.KickPlayerInfo) bool {
if info == nil {
return false
}
convId, exist := f.getConvIdByUserId(info.UserId)
if !exist {
return false
}
f.kcpEventInput <- &net.KcpEvent{
ConvId: convId,
EventId: net.KcpConnForceClose,
EventMessage: info.Reason,
}
return true
}
// 获取网关在线玩家信息
func (f *ForwardManager) GetOnlineUser(uid uint32) (list *gm.OnlineUserList) {
list = &gm.OnlineUserList{
UserList: make([]*gm.OnlineUserInfo, 0),
}
if uid == 0 {
// 获取全部玩家
f.convUserIdMapLock.RLock()
f.convAddrMapLock.RLock()
for convId, userId := range f.convUserIdMap {
addr := f.convAddrMap[convId]
info := &gm.OnlineUserInfo{
Uid: userId,
ConvId: convId,
Addr: addr,
}
list.UserList = append(list.UserList, info)
}
f.convAddrMapLock.RUnlock()
f.convUserIdMapLock.RUnlock()
} else {
// 获取指定uid玩家
convId, exist := f.getConvIdByUserId(uid)
if !exist {
return list
}
addr, exist := f.getAddrByConvId(convId)
if !exist {
return list
}
info := &gm.OnlineUserInfo{
Uid: uid,
ConvId: convId,
Addr: addr,
}
list.UserList = append(list.UserList, info)
}
return list
}
// 用户密码改变
func (f *ForwardManager) UserPasswordChange(uid uint32) bool {
// dispatch登录态失效
_, err := f.dao.UpdateAccountFieldByFieldName("uid", uid, "token", "")
if err != nil {
return false
}
// 游戏内登录态失效
account, err := f.dao.QueryAccountByField("uid", uid)
if err != nil {
return false
}
if account == nil {
return false
}
convId, exist := f.getConvIdByUserId(uint32(account.PlayerID))
if !exist {
return true
}
f.kcpEventInput <- &net.KcpEvent{
ConvId: convId,
EventId: net.KcpConnForceClose,
EventMessage: uint32(kcp.EnetAccountPasswordChange),
}
return true
}
// 封号
func (f *ForwardManager) ForbidUser(info *gm.ForbidUserInfo) bool {
if info == nil {
return false
}
// 写入账号封禁信息
_, err := f.dao.UpdateAccountFieldByFieldName("uid", info.UserId, "forbid", true)
if err != nil {
return false
}
_, err = f.dao.UpdateAccountFieldByFieldName("uid", info.UserId, "forbidEndTime", info.ForbidEndTime)
if err != nil {
return false
}
// 游戏强制下线
account, err := f.dao.QueryAccountByField("uid", info.UserId)
if err != nil {
return false
}
if account == nil {
return false
}
convId, exist := f.getConvIdByUserId(uint32(account.PlayerID))
if !exist {
return true
}
f.kcpEventInput <- &net.KcpEvent{
ConvId: convId,
EventId: net.KcpConnForceClose,
EventMessage: uint32(kcp.EnetServerKillClient),
}
return true
}
// 解封
func (f *ForwardManager) UnForbidUser(uid uint32) bool {
// 解除账号封禁
_, err := f.dao.UpdateAccountFieldByFieldName("uid", uid, "forbid", false)
if err != nil {
return false
}
return true
}

View File

@@ -0,0 +1,204 @@
package forward
import (
"bytes"
"encoding/base64"
"encoding/binary"
"flswld.com/common/utils/endec"
"flswld.com/gate-hk4e-api/proto"
"flswld.com/logger"
"gate-hk4e/kcp"
"gate-hk4e/net"
"strconv"
"strings"
"time"
)
func (f *ForwardManager) getPlayerToken(convId uint64, req *proto.GetPlayerTokenReq) (rsp *proto.GetPlayerTokenRsp) {
uidStr := req.AccountUid
uid, err := strconv.ParseInt(uidStr, 10, 64)
if err != nil {
logger.LOG.Error("parse uid error: %v", err)
return nil
}
account, err := f.dao.QueryAccountByField("uid", uid)
if err != nil {
logger.LOG.Error("query account error: %v", err)
return nil
}
if account == nil {
logger.LOG.Error("account is nil")
return nil
}
if account.ComboToken != req.AccountToken {
logger.LOG.Error("token error")
return nil
}
// comboToken验证成功
if account.Forbid {
if account.ForbidEndTime > uint64(time.Now().Unix()) {
// 封号通知
rsp = new(proto.GetPlayerTokenRsp)
rsp.Uid = uint32(account.PlayerID)
rsp.IsProficientPlayer = true
rsp.Retcode = 21
rsp.Msg = "FORBID_CHEATING_PLUGINS"
//rsp.BlackUidEndTime = 2051193600 // 2035-01-01 00:00:00
rsp.BlackUidEndTime = uint32(account.ForbidEndTime)
rsp.RegPlatform = 3
rsp.CountryCode = "US"
addr, exist := f.getAddrByConvId(convId)
if !exist {
logger.LOG.Error("can not find addr by convId")
return nil
}
split := strings.Split(addr, ":")
rsp.ClientIpStr = split[0]
return rsp
} else {
account.Forbid = false
_, err := f.dao.UpdateAccountFieldByFieldName("uid", account.Uid, "forbid", false)
if err != nil {
logger.LOG.Error("update db error: %v", err)
return nil
}
}
}
oldConvId, oldExist := f.getConvIdByUserId(uint32(account.PlayerID))
if oldExist {
// 顶号
f.kcpEventInput <- &net.KcpEvent{
ConvId: oldConvId,
EventId: net.KcpConnForceClose,
EventMessage: uint32(kcp.EnetServerRelogin),
}
}
f.setUserIdByConvId(convId, uint32(account.PlayerID))
f.setConvIdByUserId(uint32(account.PlayerID), convId)
f.setConnState(convId, ConnWaitLogin)
// 返回响应
rsp = new(proto.GetPlayerTokenRsp)
rsp.Uid = uint32(account.PlayerID)
rsp.Token = account.ComboToken
rsp.AccountType = 1
// TODO 要确定一下新注册的号这个值该返回什么
rsp.IsProficientPlayer = true
rsp.SecretKeySeed = 11468049314633205968
rsp.SecurityCmdBuffer = f.secretKeyBuffer
rsp.PlatformType = 3
rsp.ChannelId = 1
rsp.CountryCode = "US"
rsp.ClientVersionRandomKey = "c25-314dd05b0b5f"
rsp.RegPlatform = 3
addr, exist := f.getAddrByConvId(convId)
if !exist {
logger.LOG.Error("can not find addr by convId")
return nil
}
split := strings.Split(addr, ":")
rsp.ClientIpStr = split[0]
if req.GetKeyId() != 0 {
// pre check
logger.LOG.Debug("do hk4e 2.8 rsa logic")
keyId := strconv.Itoa(int(req.GetKeyId()))
encPubPrivKey, exist := f.encRsaKeyMap[keyId]
if !exist {
logger.LOG.Error("can not found key id: %v", keyId)
return
}
pubKey, err := endec.RsaParsePubKeyByPrivKey(encPubPrivKey)
if err != nil {
logger.LOG.Error("parse rsa pub key error: %v", err)
return nil
}
signPrivkey, err := endec.RsaParsePrivKey(f.signRsaKey)
if err != nil {
logger.LOG.Error("parse rsa priv key error: %v", err)
return nil
}
clientSeedBase64 := req.GetClientSeed()
clientSeedEnc, err := base64.StdEncoding.DecodeString(clientSeedBase64)
if err != nil {
logger.LOG.Error("parse client seed base64 error: %v", err)
return nil
}
// create error rsp info
clientSeedEncCopy := make([]byte, len(clientSeedEnc))
copy(clientSeedEncCopy, clientSeedEnc)
endec.Xor(clientSeedEncCopy, []byte{0x9f, 0x26, 0xb2, 0x17, 0x61, 0x5f, 0xc8, 0x00})
rsp.EncryptedSeed = base64.StdEncoding.EncodeToString(clientSeedEncCopy)
rsp.SeedSignature = "bm90aGluZyBoZXJl"
// do
clientSeed, err := endec.RsaDecrypt(clientSeedEnc, signPrivkey)
if err != nil {
logger.LOG.Error("rsa dec error: %v", err)
return rsp
}
clientSeedUint64 := uint64(0)
err = binary.Read(bytes.NewReader(clientSeed), binary.BigEndian, &clientSeedUint64)
if err != nil {
logger.LOG.Error("parse client seed to uint64 error: %v", err)
return rsp
}
seedUint64 := uint64(11468049314633205968) ^ clientSeedUint64
seedBuf := new(bytes.Buffer)
err = binary.Write(seedBuf, binary.BigEndian, seedUint64)
if err != nil {
logger.LOG.Error("conv seed uint64 to bytes error: %v", err)
return rsp
}
seed := seedBuf.Bytes()
seedEnc, err := endec.RsaEncrypt(seed, pubKey)
if err != nil {
logger.LOG.Error("rsa enc error: %v", err)
return rsp
}
seedSign, err := endec.RsaSign(seed, signPrivkey)
if err != nil {
logger.LOG.Error("rsa sign error: %v", err)
return rsp
}
rsp.EncryptedSeed = base64.StdEncoding.EncodeToString(seedEnc)
rsp.SeedSignature = base64.StdEncoding.EncodeToString(seedSign)
}
return rsp
}
func (f *ForwardManager) playerLogin(convId uint64, req *proto.PlayerLoginReq) (rsp *proto.PlayerLoginRsp) {
userId, exist := f.getUserIdByConvId(convId)
if !exist {
logger.LOG.Error("can not find userId by convId")
return nil
}
account, err := f.dao.QueryAccountByField("playerID", userId)
if err != nil {
logger.LOG.Error("query account error: %v", err)
return nil
}
if account == nil {
logger.LOG.Error("account is nil")
return nil
}
if account.ComboToken != req.Token {
logger.LOG.Error("token error")
return nil
}
// comboToken验证成功
f.setConnState(convId, ConnAlive)
// 返回响应
rsp = new(proto.PlayerLoginRsp)
rsp.IsUseAbilityHash = true
rsp.AbilityHashCode = 1844674
rsp.GameBiz = "hk4e_global"
rsp.ClientDataVersion = f.regionCurr.RegionInfo.ClientDataVersion
rsp.ClientSilenceDataVersion = f.regionCurr.RegionInfo.ClientSilenceDataVersion
rsp.ClientMd5 = f.regionCurr.RegionInfo.ClientDataMd5
rsp.ClientSilenceMd5 = f.regionCurr.RegionInfo.ClientSilenceDataMd5
rsp.ResVersionConfig = f.regionCurr.RegionInfo.ResVersionConfig
rsp.ClientVersionSuffix = f.regionCurr.RegionInfo.ClientVersionSuffix
rsp.ClientSilenceVersionSuffix = f.regionCurr.RegionInfo.ClientSilenceVersionSuffix
rsp.IsScOpen = false
rsp.RegisterCps = "mihoyo"
rsp.CountryCode = "US"
return rsp
}

94
gate-hk4e/go.mod Normal file
View File

@@ -0,0 +1,94 @@
module gate-hk4e
go 1.19
// annie
require flswld.com/common v0.0.0-incompatible
replace flswld.com/common => ../common
require flswld.com/logger v0.0.0-incompatible
replace flswld.com/logger => ../logger
require flswld.com/air-api v0.0.0-incompatible // indirect
replace flswld.com/air-api => ../air-api
require flswld.com/light v0.0.0-incompatible
replace flswld.com/light => ../light
require flswld.com/gate-hk4e-api v0.0.0-incompatible
replace flswld.com/gate-hk4e-api => ../gate-hk4e-api
require flswld.com/annie-user-api v0.0.0-incompatible
replace flswld.com/annie-user-api => ../service/annie-user-api
// kcp
require (
github.com/klauspost/reedsolomon v1.9.14
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.7.0
github.com/templexxx/xorsimd v0.4.1
github.com/tjfoc/gmsm v1.4.1
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9
)
// protobuf
require google.golang.org/protobuf v1.28.0
// gin
require github.com/gin-gonic/gin v1.6.3
// mongodb
require go.mongodb.org/mongo-driver v1.8.3
// nats
require github.com/nats-io/nats.go v1.16.0
// msgpack
require github.com/vmihailenco/msgpack/v5 v5.3.5
// statsviz
require github.com/arl/statsviz v0.5.1
require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.2.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang/protobuf v1.5.0 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/json-iterator/go v1.1.9 // indirect
github.com/klauspost/compress v1.14.4 // indirect
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
github.com/nats-io/nats-server/v2 v2.8.4 // indirect
github.com/nats-io/nkeys v0.3.0 // indirect
github.com/nats-io/nuid v1.0.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/templexxx/cpu v0.0.1 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.0.2 // indirect
github.com/xdg-go/stringprep v1.0.2 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 // indirect
golang.org/x/text v0.3.6 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)

203
gate-hk4e/go.sum Normal file
View File

@@ -0,0 +1,203 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/arl/statsviz v0.5.1 h1:3HY0ZEB738JtguWsD1Tf1pFJZiCcWUmYRq/3OTYKaSI=
github.com/arl/statsviz v0.5.1/go.mod h1:zDnjgRblGm1Dyd7J5YlbH7gM1/+HRC+SfkhZhQb5AnM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.14.4 h1:eijASRJcobkVtSt81Olfh7JX43osYLwy5krOJo6YEu4=
github.com/klauspost/compress v1.14.4/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/reedsolomon v1.9.14 h1:vkPCIhFMn2VdktLUcugqsU4vcLXN3dAhVd1uWA+TDD8=
github.com/klauspost/reedsolomon v1.9.14/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/nats-io/jwt/v2 v2.2.1-0.20220330180145-442af02fd36a h1:lem6QCvxR0Y28gth9P+wV2K/zYUUAkJ+55U8cpS0p5I=
github.com/nats-io/nats-server/v2 v2.8.4 h1:0jQzze1T9mECg8YZEl8+WYUXb9JKluJfCBriPUtluB4=
github.com/nats-io/nats-server/v2 v2.8.4/go.mod h1:8zZa+Al3WsESfmgSs98Fi06dRWLH5Bnq90m5bKD/eT4=
github.com/nats-io/nats.go v1.16.0 h1:zvLE7fGBQYW6MWaFaRdsgm9qT39PJDQoju+DS8KsO1g=
github.com/nats-io/nats.go v1.16.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
github.com/nats-io/nkeys v0.3.0 h1:cgM5tL53EvYRU+2YLXIK0G2mJtK12Ft9oeooSZMA2G8=
github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/templexxx/cpu v0.0.1 h1:hY4WdLOgKdc8y13EYklu9OUTXik80BkxHoWvTO6MQQY=
github.com/templexxx/cpu v0.0.1/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk=
github.com/templexxx/xorsimd v0.4.1 h1:iUZcywbOYDRAZUasAs2eSCUW8eobuZDy0I9FJiORkVg=
github.com/templexxx/xorsimd v0.4.1/go.mod h1:W+ffZz8jJMH2SXwuKu9WhygqBMbFnp14G2fqEr8qaNo=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2 h1:akYIkZ28e6A96dkWNJQu3nmCzH3YfwMPQExUYDaRv7w=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
github.com/xdg-go/stringprep v1.0.2 h1:6iq84/ryjjeRmMJwxutI51F2GIPlP5BfTvXHeYjyhBc=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E=
github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4=
go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 h1:0qxwC5n+ttVOINCBeRHO0nq9X7uy8SDsPoi5OaCdIEI=
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320 h1:0jf+tOCoZ3LyutmCOWpVni1chK4VfFLhRsDK7MhqGRY=
golang.org/x/sys v0.0.0-20220111092808-5a964db01320/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
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=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

64
gate-hk4e/kcp/autotune.go Normal file
View File

@@ -0,0 +1,64 @@
package kcp
const maxAutoTuneSamples = 258
// pulse represents a 0/1 signal with time sequence
type pulse struct {
bit bool // 0 or 1
seq uint32 // sequence of the signal
}
// autoTune object
type autoTune struct {
pulses [maxAutoTuneSamples]pulse
}
// Sample adds a signal sample to the pulse buffer
func (tune *autoTune) Sample(bit bool, seq uint32) {
tune.pulses[seq%maxAutoTuneSamples] = pulse{bit, seq}
}
// Find a period for a given signal
// returns -1 if not found
//
// --- ------
// | |
// |______________|
// Period
// Falling Edge Rising Edge
func (tune *autoTune) FindPeriod(bit bool) int {
// last pulse and initial index setup
lastPulse := tune.pulses[0]
idx := 1
// left edge
var leftEdge int
for ; idx < len(tune.pulses); idx++ {
if lastPulse.bit != bit && tune.pulses[idx].bit == bit { // edge found
if lastPulse.seq+1 == tune.pulses[idx].seq { // ensure edge continuity
leftEdge = idx
break
}
}
lastPulse = tune.pulses[idx]
}
// right edge
var rightEdge int
lastPulse = tune.pulses[leftEdge]
idx = leftEdge + 1
for ; idx < len(tune.pulses); idx++ {
if lastPulse.seq+1 == tune.pulses[idx].seq { // ensure pulses in this level monotonic
if lastPulse.bit == bit && tune.pulses[idx].bit != bit { // edge found
rightEdge = idx
break
}
} else {
return -1
}
lastPulse = tune.pulses[idx]
}
return rightEdge - leftEdge
}

View File

@@ -0,0 +1,47 @@
package kcp
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestAutoTune(t *testing.T) {
signals := []uint32{0, 0, 0, 0, 0, 0}
tune := autoTune{}
for i := 0; i < len(signals); i++ {
if signals[i] == 0 {
tune.Sample(false, uint32(i))
} else {
tune.Sample(true, uint32(i))
}
}
assert.Equal(t, -1, tune.FindPeriod(false))
assert.Equal(t, -1, tune.FindPeriod(true))
signals = []uint32{1, 0, 1, 0, 0, 1}
tune = autoTune{}
for i := 0; i < len(signals); i++ {
if signals[i] == 0 {
tune.Sample(false, uint32(i))
} else {
tune.Sample(true, uint32(i))
}
}
assert.Equal(t, 1, tune.FindPeriod(false))
assert.Equal(t, 1, tune.FindPeriod(true))
signals = []uint32{1, 0, 0, 0, 0, 1}
tune = autoTune{}
for i := 0; i < len(signals); i++ {
if signals[i] == 0 {
tune.Sample(false, uint32(i))
} else {
tune.Sample(true, uint32(i))
}
}
assert.Equal(t, -1, tune.FindPeriod(true))
assert.Equal(t, 4, tune.FindPeriod(false))
}

View File

@@ -0,0 +1,12 @@
package kcp
import "golang.org/x/net/ipv4"
const (
batchSize = 16
)
type batchConn interface {
WriteBatch(ms []ipv4.Message, flags int) (int, error)
ReadBatch(ms []ipv4.Message, flags int) (int, error)
}

618
gate-hk4e/kcp/crypt.go Normal file
View File

@@ -0,0 +1,618 @@
package kcp
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"crypto/sha1"
"unsafe"
xor "github.com/templexxx/xorsimd"
"github.com/tjfoc/gmsm/sm4"
"golang.org/x/crypto/blowfish"
"golang.org/x/crypto/cast5"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/salsa20"
"golang.org/x/crypto/tea"
"golang.org/x/crypto/twofish"
"golang.org/x/crypto/xtea"
)
var (
initialVector = []byte{167, 115, 79, 156, 18, 172, 27, 1, 164, 21, 242, 193, 252, 120, 230, 107}
saltxor = `sH3CIVoF#rWLtJo6`
)
// BlockCrypt defines encryption/decryption methods for a given byte slice.
// Notes on implementing: the data to be encrypted contains a builtin
// nonce at the first 16 bytes
type BlockCrypt interface {
// Encrypt encrypts the whole block in src into dst.
// Dst and src may point at the same memory.
Encrypt(dst, src []byte)
// Decrypt decrypts the whole block in src into dst.
// Dst and src may point at the same memory.
Decrypt(dst, src []byte)
}
type salsa20BlockCrypt struct {
key [32]byte
}
// NewSalsa20BlockCrypt https://en.wikipedia.org/wiki/Salsa20
func NewSalsa20BlockCrypt(key []byte) (BlockCrypt, error) {
c := new(salsa20BlockCrypt)
copy(c.key[:], key)
return c, nil
}
func (c *salsa20BlockCrypt) Encrypt(dst, src []byte) {
salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
copy(dst[:8], src[:8])
}
func (c *salsa20BlockCrypt) Decrypt(dst, src []byte) {
salsa20.XORKeyStream(dst[8:], src[8:], src[:8], &c.key)
copy(dst[:8], src[:8])
}
type sm4BlockCrypt struct {
encbuf [sm4.BlockSize]byte // 64bit alignment enc/dec buffer
decbuf [2 * sm4.BlockSize]byte
block cipher.Block
}
// NewSM4BlockCrypt https://github.com/tjfoc/gmsm/tree/master/sm4
func NewSM4BlockCrypt(key []byte) (BlockCrypt, error) {
c := new(sm4BlockCrypt)
block, err := sm4.NewCipher(key)
if err != nil {
return nil, err
}
c.block = block
return c, nil
}
func (c *sm4BlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
func (c *sm4BlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
type twofishBlockCrypt struct {
encbuf [twofish.BlockSize]byte
decbuf [2 * twofish.BlockSize]byte
block cipher.Block
}
// NewTwofishBlockCrypt https://en.wikipedia.org/wiki/Twofish
func NewTwofishBlockCrypt(key []byte) (BlockCrypt, error) {
c := new(twofishBlockCrypt)
block, err := twofish.NewCipher(key)
if err != nil {
return nil, err
}
c.block = block
return c, nil
}
func (c *twofishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
func (c *twofishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
type tripleDESBlockCrypt struct {
encbuf [des.BlockSize]byte
decbuf [2 * des.BlockSize]byte
block cipher.Block
}
// NewTripleDESBlockCrypt https://en.wikipedia.org/wiki/Triple_DES
func NewTripleDESBlockCrypt(key []byte) (BlockCrypt, error) {
c := new(tripleDESBlockCrypt)
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
c.block = block
return c, nil
}
func (c *tripleDESBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
func (c *tripleDESBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
type cast5BlockCrypt struct {
encbuf [cast5.BlockSize]byte
decbuf [2 * cast5.BlockSize]byte
block cipher.Block
}
// NewCast5BlockCrypt https://en.wikipedia.org/wiki/CAST-128
func NewCast5BlockCrypt(key []byte) (BlockCrypt, error) {
c := new(cast5BlockCrypt)
block, err := cast5.NewCipher(key)
if err != nil {
return nil, err
}
c.block = block
return c, nil
}
func (c *cast5BlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
func (c *cast5BlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
type blowfishBlockCrypt struct {
encbuf [blowfish.BlockSize]byte
decbuf [2 * blowfish.BlockSize]byte
block cipher.Block
}
// NewBlowfishBlockCrypt https://en.wikipedia.org/wiki/Blowfish_(cipher)
func NewBlowfishBlockCrypt(key []byte) (BlockCrypt, error) {
c := new(blowfishBlockCrypt)
block, err := blowfish.NewCipher(key)
if err != nil {
return nil, err
}
c.block = block
return c, nil
}
func (c *blowfishBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
func (c *blowfishBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
type aesBlockCrypt struct {
encbuf [aes.BlockSize]byte
decbuf [2 * aes.BlockSize]byte
block cipher.Block
}
// NewAESBlockCrypt https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
func NewAESBlockCrypt(key []byte) (BlockCrypt, error) {
c := new(aesBlockCrypt)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
c.block = block
return c, nil
}
func (c *aesBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
func (c *aesBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
type teaBlockCrypt struct {
encbuf [tea.BlockSize]byte
decbuf [2 * tea.BlockSize]byte
block cipher.Block
}
// NewTEABlockCrypt https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm
func NewTEABlockCrypt(key []byte) (BlockCrypt, error) {
c := new(teaBlockCrypt)
block, err := tea.NewCipherWithRounds(key, 16)
if err != nil {
return nil, err
}
c.block = block
return c, nil
}
func (c *teaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
func (c *teaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
type xteaBlockCrypt struct {
encbuf [xtea.BlockSize]byte
decbuf [2 * xtea.BlockSize]byte
block cipher.Block
}
// NewXTEABlockCrypt https://en.wikipedia.org/wiki/XTEA
func NewXTEABlockCrypt(key []byte) (BlockCrypt, error) {
c := new(xteaBlockCrypt)
block, err := xtea.NewCipher(key)
if err != nil {
return nil, err
}
c.block = block
return c, nil
}
func (c *xteaBlockCrypt) Encrypt(dst, src []byte) { encrypt(c.block, dst, src, c.encbuf[:]) }
func (c *xteaBlockCrypt) Decrypt(dst, src []byte) { decrypt(c.block, dst, src, c.decbuf[:]) }
type simpleXORBlockCrypt struct {
xortbl []byte
}
// NewSimpleXORBlockCrypt simple xor with key expanding
func NewSimpleXORBlockCrypt(key []byte) (BlockCrypt, error) {
c := new(simpleXORBlockCrypt)
c.xortbl = pbkdf2.Key(key, []byte(saltxor), 32, mtuLimit, sha1.New)
return c, nil
}
func (c *simpleXORBlockCrypt) Encrypt(dst, src []byte) { xor.Bytes(dst, src, c.xortbl) }
func (c *simpleXORBlockCrypt) Decrypt(dst, src []byte) { xor.Bytes(dst, src, c.xortbl) }
type noneBlockCrypt struct{}
// NewNoneBlockCrypt does nothing but copying
func NewNoneBlockCrypt(key []byte) (BlockCrypt, error) {
return new(noneBlockCrypt), nil
}
func (c *noneBlockCrypt) Encrypt(dst, src []byte) { copy(dst, src) }
func (c *noneBlockCrypt) Decrypt(dst, src []byte) { copy(dst, src) }
// packet encryption with local CFB mode
func encrypt(block cipher.Block, dst, src, buf []byte) {
switch block.BlockSize() {
case 8:
encrypt8(block, dst, src, buf)
case 16:
encrypt16(block, dst, src, buf)
default:
panic("unsupported cipher block size")
}
}
// optimized encryption for the ciphers which works in 8-bytes
func encrypt8(block cipher.Block, dst, src, buf []byte) {
tbl := buf[:8]
block.Encrypt(tbl, initialVector)
n := len(src) / 8
base := 0
repeat := n / 8
left := n % 8
ptr_tbl := (*uint64)(unsafe.Pointer(&tbl[0]))
for i := 0; i < repeat; i++ {
s := src[base:][0:64]
d := dst[base:][0:64]
// 1
*(*uint64)(unsafe.Pointer(&d[0])) = *(*uint64)(unsafe.Pointer(&s[0])) ^ *ptr_tbl
block.Encrypt(tbl, d[0:8])
// 2
*(*uint64)(unsafe.Pointer(&d[8])) = *(*uint64)(unsafe.Pointer(&s[8])) ^ *ptr_tbl
block.Encrypt(tbl, d[8:16])
// 3
*(*uint64)(unsafe.Pointer(&d[16])) = *(*uint64)(unsafe.Pointer(&s[16])) ^ *ptr_tbl
block.Encrypt(tbl, d[16:24])
// 4
*(*uint64)(unsafe.Pointer(&d[24])) = *(*uint64)(unsafe.Pointer(&s[24])) ^ *ptr_tbl
block.Encrypt(tbl, d[24:32])
// 5
*(*uint64)(unsafe.Pointer(&d[32])) = *(*uint64)(unsafe.Pointer(&s[32])) ^ *ptr_tbl
block.Encrypt(tbl, d[32:40])
// 6
*(*uint64)(unsafe.Pointer(&d[40])) = *(*uint64)(unsafe.Pointer(&s[40])) ^ *ptr_tbl
block.Encrypt(tbl, d[40:48])
// 7
*(*uint64)(unsafe.Pointer(&d[48])) = *(*uint64)(unsafe.Pointer(&s[48])) ^ *ptr_tbl
block.Encrypt(tbl, d[48:56])
// 8
*(*uint64)(unsafe.Pointer(&d[56])) = *(*uint64)(unsafe.Pointer(&s[56])) ^ *ptr_tbl
block.Encrypt(tbl, d[56:64])
base += 64
}
switch left {
case 7:
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
block.Encrypt(tbl, dst[base:])
base += 8
fallthrough
case 6:
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
block.Encrypt(tbl, dst[base:])
base += 8
fallthrough
case 5:
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
block.Encrypt(tbl, dst[base:])
base += 8
fallthrough
case 4:
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
block.Encrypt(tbl, dst[base:])
base += 8
fallthrough
case 3:
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
block.Encrypt(tbl, dst[base:])
base += 8
fallthrough
case 2:
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
block.Encrypt(tbl, dst[base:])
base += 8
fallthrough
case 1:
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *ptr_tbl
block.Encrypt(tbl, dst[base:])
base += 8
fallthrough
case 0:
xorBytes(dst[base:], src[base:], tbl)
}
}
// optimized encryption for the ciphers which works in 16-bytes
func encrypt16(block cipher.Block, dst, src, buf []byte) {
tbl := buf[:16]
block.Encrypt(tbl, initialVector)
n := len(src) / 16
base := 0
repeat := n / 8
left := n % 8
for i := 0; i < repeat; i++ {
s := src[base:][0:128]
d := dst[base:][0:128]
// 1
xor.Bytes16Align(d[0:16], s[0:16], tbl)
block.Encrypt(tbl, d[0:16])
// 2
xor.Bytes16Align(d[16:32], s[16:32], tbl)
block.Encrypt(tbl, d[16:32])
// 3
xor.Bytes16Align(d[32:48], s[32:48], tbl)
block.Encrypt(tbl, d[32:48])
// 4
xor.Bytes16Align(d[48:64], s[48:64], tbl)
block.Encrypt(tbl, d[48:64])
// 5
xor.Bytes16Align(d[64:80], s[64:80], tbl)
block.Encrypt(tbl, d[64:80])
// 6
xor.Bytes16Align(d[80:96], s[80:96], tbl)
block.Encrypt(tbl, d[80:96])
// 7
xor.Bytes16Align(d[96:112], s[96:112], tbl)
block.Encrypt(tbl, d[96:112])
// 8
xor.Bytes16Align(d[112:128], s[112:128], tbl)
block.Encrypt(tbl, d[112:128])
base += 128
}
switch left {
case 7:
xor.Bytes16Align(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 6:
xor.Bytes16Align(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 5:
xor.Bytes16Align(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 4:
xor.Bytes16Align(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 3:
xor.Bytes16Align(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 2:
xor.Bytes16Align(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 1:
xor.Bytes16Align(dst[base:], src[base:], tbl)
block.Encrypt(tbl, dst[base:])
base += 16
fallthrough
case 0:
xorBytes(dst[base:], src[base:], tbl)
}
}
// decryption
func decrypt(block cipher.Block, dst, src, buf []byte) {
switch block.BlockSize() {
case 8:
decrypt8(block, dst, src, buf)
case 16:
decrypt16(block, dst, src, buf)
default:
panic("unsupported cipher block size")
}
}
// decrypt 8 bytes block, all byte slices are supposed to be 64bit aligned
func decrypt8(block cipher.Block, dst, src, buf []byte) {
tbl := buf[0:8]
next := buf[8:16]
block.Encrypt(tbl, initialVector)
n := len(src) / 8
base := 0
repeat := n / 8
left := n % 8
ptr_tbl := (*uint64)(unsafe.Pointer(&tbl[0]))
ptr_next := (*uint64)(unsafe.Pointer(&next[0]))
for i := 0; i < repeat; i++ {
s := src[base:][0:64]
d := dst[base:][0:64]
// 1
block.Encrypt(next, s[0:8])
*(*uint64)(unsafe.Pointer(&d[0])) = *(*uint64)(unsafe.Pointer(&s[0])) ^ *ptr_tbl
// 2
block.Encrypt(tbl, s[8:16])
*(*uint64)(unsafe.Pointer(&d[8])) = *(*uint64)(unsafe.Pointer(&s[8])) ^ *ptr_next
// 3
block.Encrypt(next, s[16:24])
*(*uint64)(unsafe.Pointer(&d[16])) = *(*uint64)(unsafe.Pointer(&s[16])) ^ *ptr_tbl
// 4
block.Encrypt(tbl, s[24:32])
*(*uint64)(unsafe.Pointer(&d[24])) = *(*uint64)(unsafe.Pointer(&s[24])) ^ *ptr_next
// 5
block.Encrypt(next, s[32:40])
*(*uint64)(unsafe.Pointer(&d[32])) = *(*uint64)(unsafe.Pointer(&s[32])) ^ *ptr_tbl
// 6
block.Encrypt(tbl, s[40:48])
*(*uint64)(unsafe.Pointer(&d[40])) = *(*uint64)(unsafe.Pointer(&s[40])) ^ *ptr_next
// 7
block.Encrypt(next, s[48:56])
*(*uint64)(unsafe.Pointer(&d[48])) = *(*uint64)(unsafe.Pointer(&s[48])) ^ *ptr_tbl
// 8
block.Encrypt(tbl, s[56:64])
*(*uint64)(unsafe.Pointer(&d[56])) = *(*uint64)(unsafe.Pointer(&s[56])) ^ *ptr_next
base += 64
}
switch left {
case 7:
block.Encrypt(next, src[base:])
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
tbl, next = next, tbl
base += 8
fallthrough
case 6:
block.Encrypt(next, src[base:])
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
tbl, next = next, tbl
base += 8
fallthrough
case 5:
block.Encrypt(next, src[base:])
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
tbl, next = next, tbl
base += 8
fallthrough
case 4:
block.Encrypt(next, src[base:])
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
tbl, next = next, tbl
base += 8
fallthrough
case 3:
block.Encrypt(next, src[base:])
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
tbl, next = next, tbl
base += 8
fallthrough
case 2:
block.Encrypt(next, src[base:])
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
tbl, next = next, tbl
base += 8
fallthrough
case 1:
block.Encrypt(next, src[base:])
*(*uint64)(unsafe.Pointer(&dst[base])) = *(*uint64)(unsafe.Pointer(&src[base])) ^ *(*uint64)(unsafe.Pointer(&tbl[0]))
tbl, next = next, tbl
base += 8
fallthrough
case 0:
xorBytes(dst[base:], src[base:], tbl)
}
}
func decrypt16(block cipher.Block, dst, src, buf []byte) {
tbl := buf[0:16]
next := buf[16:32]
block.Encrypt(tbl, initialVector)
n := len(src) / 16
base := 0
repeat := n / 8
left := n % 8
for i := 0; i < repeat; i++ {
s := src[base:][0:128]
d := dst[base:][0:128]
// 1
block.Encrypt(next, s[0:16])
xor.Bytes16Align(d[0:16], s[0:16], tbl)
// 2
block.Encrypt(tbl, s[16:32])
xor.Bytes16Align(d[16:32], s[16:32], next)
// 3
block.Encrypt(next, s[32:48])
xor.Bytes16Align(d[32:48], s[32:48], tbl)
// 4
block.Encrypt(tbl, s[48:64])
xor.Bytes16Align(d[48:64], s[48:64], next)
// 5
block.Encrypt(next, s[64:80])
xor.Bytes16Align(d[64:80], s[64:80], tbl)
// 6
block.Encrypt(tbl, s[80:96])
xor.Bytes16Align(d[80:96], s[80:96], next)
// 7
block.Encrypt(next, s[96:112])
xor.Bytes16Align(d[96:112], s[96:112], tbl)
// 8
block.Encrypt(tbl, s[112:128])
xor.Bytes16Align(d[112:128], s[112:128], next)
base += 128
}
switch left {
case 7:
block.Encrypt(next, src[base:])
xor.Bytes16Align(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 6:
block.Encrypt(next, src[base:])
xor.Bytes16Align(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 5:
block.Encrypt(next, src[base:])
xor.Bytes16Align(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 4:
block.Encrypt(next, src[base:])
xor.Bytes16Align(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 3:
block.Encrypt(next, src[base:])
xor.Bytes16Align(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 2:
block.Encrypt(next, src[base:])
xor.Bytes16Align(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 1:
block.Encrypt(next, src[base:])
xor.Bytes16Align(dst[base:], src[base:], tbl)
tbl, next = next, tbl
base += 16
fallthrough
case 0:
xorBytes(dst[base:], src[base:], tbl)
}
}
// per bytes xors
func xorBytes(dst, a, b []byte) int {
n := len(a)
if len(b) < n {
n = len(b)
}
if n == 0 {
return 0
}
for i := 0; i < n; i++ {
dst[i] = a[i] ^ b[i]
}
return n
}

289
gate-hk4e/kcp/crypt_test.go Normal file
View File

@@ -0,0 +1,289 @@
package kcp
import (
"bytes"
"crypto/aes"
"crypto/md5"
"crypto/rand"
"crypto/sha1"
"hash/crc32"
"io"
"testing"
)
func TestSM4(t *testing.T) {
bc, err := NewSM4BlockCrypt(pass[:16])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func TestAES(t *testing.T) {
bc, err := NewAESBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func TestTEA(t *testing.T) {
bc, err := NewTEABlockCrypt(pass[:16])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func TestXOR(t *testing.T) {
bc, err := NewSimpleXORBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func TestBlowfish(t *testing.T) {
bc, err := NewBlowfishBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func TestNone(t *testing.T) {
bc, err := NewNoneBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func TestCast5(t *testing.T) {
bc, err := NewCast5BlockCrypt(pass[:16])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func Test3DES(t *testing.T) {
bc, err := NewTripleDESBlockCrypt(pass[:24])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func TestTwofish(t *testing.T) {
bc, err := NewTwofishBlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func TestXTEA(t *testing.T) {
bc, err := NewXTEABlockCrypt(pass[:16])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func TestSalsa20(t *testing.T) {
bc, err := NewSalsa20BlockCrypt(pass[:32])
if err != nil {
t.Fatal(err)
}
cryptTest(t, bc)
}
func cryptTest(t *testing.T, bc BlockCrypt) {
data := make([]byte, mtuLimit)
io.ReadFull(rand.Reader, data)
dec := make([]byte, mtuLimit)
enc := make([]byte, mtuLimit)
bc.Encrypt(enc, data)
bc.Decrypt(dec, enc)
if !bytes.Equal(data, dec) {
t.Fail()
}
}
func BenchmarkSM4(b *testing.B) {
bc, err := NewSM4BlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkAES128(b *testing.B) {
bc, err := NewAESBlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkAES192(b *testing.B) {
bc, err := NewAESBlockCrypt(pass[:24])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkAES256(b *testing.B) {
bc, err := NewAESBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkTEA(b *testing.B) {
bc, err := NewTEABlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkXOR(b *testing.B) {
bc, err := NewSimpleXORBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkBlowfish(b *testing.B) {
bc, err := NewBlowfishBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkNone(b *testing.B) {
bc, err := NewNoneBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkCast5(b *testing.B) {
bc, err := NewCast5BlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func Benchmark3DES(b *testing.B) {
bc, err := NewTripleDESBlockCrypt(pass[:24])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkTwofish(b *testing.B) {
bc, err := NewTwofishBlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkXTEA(b *testing.B) {
bc, err := NewXTEABlockCrypt(pass[:16])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func BenchmarkSalsa20(b *testing.B) {
bc, err := NewSalsa20BlockCrypt(pass[:32])
if err != nil {
b.Fatal(err)
}
benchCrypt(b, bc)
}
func benchCrypt(b *testing.B, bc BlockCrypt) {
data := make([]byte, mtuLimit)
io.ReadFull(rand.Reader, data)
dec := make([]byte, mtuLimit)
enc := make([]byte, mtuLimit)
b.ReportAllocs()
b.SetBytes(int64(len(enc) * 2))
b.ResetTimer()
for i := 0; i < b.N; i++ {
bc.Encrypt(enc, data)
bc.Decrypt(dec, enc)
}
}
func BenchmarkCRC32(b *testing.B) {
content := make([]byte, 1024)
b.SetBytes(int64(len(content)))
for i := 0; i < b.N; i++ {
crc32.ChecksumIEEE(content)
}
}
func BenchmarkCsprngSystem(b *testing.B) {
data := make([]byte, md5.Size)
b.SetBytes(int64(len(data)))
for i := 0; i < b.N; i++ {
io.ReadFull(rand.Reader, data)
}
}
func BenchmarkCsprngMD5(b *testing.B) {
var data [md5.Size]byte
b.SetBytes(md5.Size)
for i := 0; i < b.N; i++ {
data = md5.Sum(data[:])
}
}
func BenchmarkCsprngSHA1(b *testing.B) {
var data [sha1.Size]byte
b.SetBytes(sha1.Size)
for i := 0; i < b.N; i++ {
data = sha1.Sum(data[:])
}
}
func BenchmarkCsprngNonceMD5(b *testing.B) {
var ng nonceMD5
ng.Init()
b.SetBytes(md5.Size)
data := make([]byte, md5.Size)
for i := 0; i < b.N; i++ {
ng.Fill(data)
}
}
func BenchmarkCsprngNonceAES128(b *testing.B) {
var ng nonceAES128
ng.Init()
b.SetBytes(aes.BlockSize)
data := make([]byte, aes.BlockSize)
for i := 0; i < b.N; i++ {
ng.Fill(data)
}
}

52
gate-hk4e/kcp/entropy.go Normal file
View File

@@ -0,0 +1,52 @@
package kcp
import (
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"crypto/rand"
"io"
)
// Entropy defines a entropy source
type Entropy interface {
Init()
Fill(nonce []byte)
}
// nonceMD5 nonce generator for packet header
type nonceMD5 struct {
seed [md5.Size]byte
}
func (n *nonceMD5) Init() { /*nothing required*/ }
func (n *nonceMD5) Fill(nonce []byte) {
if n.seed[0] == 0 { // entropy update
io.ReadFull(rand.Reader, n.seed[:])
}
n.seed = md5.Sum(n.seed[:])
copy(nonce, n.seed[:])
}
// nonceAES128 nonce generator for packet headers
type nonceAES128 struct {
seed [aes.BlockSize]byte
block cipher.Block
}
func (n *nonceAES128) Init() {
var key [16]byte //aes-128
io.ReadFull(rand.Reader, key[:])
io.ReadFull(rand.Reader, n.seed[:])
block, _ := aes.NewCipher(key[:])
n.block = block
}
func (n *nonceAES128) Fill(nonce []byte) {
if n.seed[0] == 0 { // entropy update
io.ReadFull(rand.Reader, n.seed[:])
}
n.block.Encrypt(n.seed[:], n.seed[:])
copy(nonce, n.seed[:])
}

381
gate-hk4e/kcp/fec.go Normal file
View File

@@ -0,0 +1,381 @@
package kcp
import (
"encoding/binary"
"sync/atomic"
"github.com/klauspost/reedsolomon"
)
const (
fecHeaderSize = 6
fecHeaderSizePlus2 = fecHeaderSize + 2 // plus 2B data size
typeData = 0xf1
typeParity = 0xf2
fecExpire = 60000
rxFECMulti = 3 // FEC keeps rxFECMulti* (dataShard+parityShard) ordered packets in memory
)
// fecPacket is a decoded FEC packet
type fecPacket []byte
func (bts fecPacket) seqid() uint32 { return binary.LittleEndian.Uint32(bts) }
func (bts fecPacket) flag() uint16 { return binary.LittleEndian.Uint16(bts[4:]) }
func (bts fecPacket) data() []byte { return bts[6:] }
// fecElement has auxcilliary time field
type fecElement struct {
fecPacket
ts uint32
}
// fecDecoder for decoding incoming packets
type fecDecoder struct {
rxlimit int // queue size limit
dataShards int
parityShards int
shardSize int
rx []fecElement // ordered receive queue
// caches
decodeCache [][]byte
flagCache []bool
// zeros
zeros []byte
// RS decoder
codec reedsolomon.Encoder
// auto tune fec parameter
autoTune autoTune
}
func newFECDecoder(dataShards, parityShards int) *fecDecoder {
if dataShards <= 0 || parityShards <= 0 {
return nil
}
dec := new(fecDecoder)
dec.dataShards = dataShards
dec.parityShards = parityShards
dec.shardSize = dataShards + parityShards
dec.rxlimit = rxFECMulti * dec.shardSize
codec, err := reedsolomon.New(dataShards, parityShards)
if err != nil {
return nil
}
dec.codec = codec
dec.decodeCache = make([][]byte, dec.shardSize)
dec.flagCache = make([]bool, dec.shardSize)
dec.zeros = make([]byte, mtuLimit)
return dec
}
// decode a fec packet
func (dec *fecDecoder) decode(in fecPacket) (recovered [][]byte) {
// sample to auto FEC tuner
if in.flag() == typeData {
dec.autoTune.Sample(true, in.seqid())
} else {
dec.autoTune.Sample(false, in.seqid())
}
// check if FEC parameters is out of sync
var shouldTune bool
if int(in.seqid())%dec.shardSize < dec.dataShards {
if in.flag() != typeData { // expect typeData
shouldTune = true
}
} else {
if in.flag() != typeParity {
shouldTune = true
}
}
if shouldTune {
autoDS := dec.autoTune.FindPeriod(true)
autoPS := dec.autoTune.FindPeriod(false)
// edges found, we can tune parameters now
if autoDS > 0 && autoPS > 0 && autoDS < 256 && autoPS < 256 {
// and make sure it's different
if autoDS != dec.dataShards || autoPS != dec.parityShards {
dec.dataShards = autoDS
dec.parityShards = autoPS
dec.shardSize = autoDS + autoPS
dec.rxlimit = rxFECMulti * dec.shardSize
codec, err := reedsolomon.New(autoDS, autoPS)
if err != nil {
return nil
}
dec.codec = codec
dec.decodeCache = make([][]byte, dec.shardSize)
dec.flagCache = make([]bool, dec.shardSize)
//log.Println("autotune to :", dec.dataShards, dec.parityShards)
}
}
}
// insertion
n := len(dec.rx) - 1
insertIdx := 0
for i := n; i >= 0; i-- {
if in.seqid() == dec.rx[i].seqid() { // de-duplicate
return nil
} else if _itimediff(in.seqid(), dec.rx[i].seqid()) > 0 { // insertion
insertIdx = i + 1
break
}
}
// make a copy
pkt := fecPacket(xmitBuf.Get().([]byte)[:len(in)])
copy(pkt, in)
elem := fecElement{pkt, currentMs()}
// insert into ordered rx queue
if insertIdx == n+1 {
dec.rx = append(dec.rx, elem)
} else {
dec.rx = append(dec.rx, fecElement{})
copy(dec.rx[insertIdx+1:], dec.rx[insertIdx:]) // shift right
dec.rx[insertIdx] = elem
}
// shard range for current packet
shardBegin := pkt.seqid() - pkt.seqid()%uint32(dec.shardSize)
shardEnd := shardBegin + uint32(dec.shardSize) - 1
// max search range in ordered queue for current shard
searchBegin := insertIdx - int(pkt.seqid()%uint32(dec.shardSize))
if searchBegin < 0 {
searchBegin = 0
}
searchEnd := searchBegin + dec.shardSize - 1
if searchEnd >= len(dec.rx) {
searchEnd = len(dec.rx) - 1
}
// re-construct datashards
if searchEnd-searchBegin+1 >= dec.dataShards {
var numshard, numDataShard, first, maxlen int
// zero caches
shards := dec.decodeCache
shardsflag := dec.flagCache
for k := range dec.decodeCache {
shards[k] = nil
shardsflag[k] = false
}
// shard assembly
for i := searchBegin; i <= searchEnd; i++ {
seqid := dec.rx[i].seqid()
if _itimediff(seqid, shardEnd) > 0 {
break
} else if _itimediff(seqid, shardBegin) >= 0 {
shards[seqid%uint32(dec.shardSize)] = dec.rx[i].data()
shardsflag[seqid%uint32(dec.shardSize)] = true
numshard++
if dec.rx[i].flag() == typeData {
numDataShard++
}
if numshard == 1 {
first = i
}
if len(dec.rx[i].data()) > maxlen {
maxlen = len(dec.rx[i].data())
}
}
}
if numDataShard == dec.dataShards {
// case 1: no loss on data shards
dec.rx = dec.freeRange(first, numshard, dec.rx)
} else if numshard >= dec.dataShards {
// case 2: loss on data shards, but it's recoverable from parity shards
for k := range shards {
if shards[k] != nil {
dlen := len(shards[k])
shards[k] = shards[k][:maxlen]
copy(shards[k][dlen:], dec.zeros)
} else if k < dec.dataShards {
shards[k] = xmitBuf.Get().([]byte)[:0]
}
}
if err := dec.codec.ReconstructData(shards); err == nil {
for k := range shards[:dec.dataShards] {
if !shardsflag[k] {
// recovered data should be recycled
recovered = append(recovered, shards[k])
}
}
}
dec.rx = dec.freeRange(first, numshard, dec.rx)
}
}
// keep rxlimit
if len(dec.rx) > dec.rxlimit {
if dec.rx[0].flag() == typeData { // track the unrecoverable data
atomic.AddUint64(&DefaultSnmp.FECShortShards, 1)
}
dec.rx = dec.freeRange(0, 1, dec.rx)
}
// timeout policy
current := currentMs()
numExpired := 0
for k := range dec.rx {
if _itimediff(current, dec.rx[k].ts) > fecExpire {
numExpired++
continue
}
break
}
if numExpired > 0 {
dec.rx = dec.freeRange(0, numExpired, dec.rx)
}
return
}
// free a range of fecPacket
func (dec *fecDecoder) freeRange(first, n int, q []fecElement) []fecElement {
for i := first; i < first+n; i++ { // recycle buffer
xmitBuf.Put([]byte(q[i].fecPacket))
}
if first == 0 && n < cap(q)/2 {
return q[n:]
}
copy(q[first:], q[first+n:])
return q[:len(q)-n]
}
// release all segments back to xmitBuf
func (dec *fecDecoder) release() {
if n := len(dec.rx); n > 0 {
dec.rx = dec.freeRange(0, n, dec.rx)
}
}
type (
// fecEncoder for encoding outgoing packets
fecEncoder struct {
dataShards int
parityShards int
shardSize int
paws uint32 // Protect Against Wrapped Sequence numbers
next uint32 // next seqid
shardCount int // count the number of datashards collected
maxSize int // track maximum data length in datashard
headerOffset int // FEC header offset
payloadOffset int // FEC payload offset
// caches
shardCache [][]byte
encodeCache [][]byte
// zeros
zeros []byte
// RS encoder
codec reedsolomon.Encoder
}
)
func newFECEncoder(dataShards, parityShards, offset int) *fecEncoder {
if dataShards <= 0 || parityShards <= 0 {
return nil
}
enc := new(fecEncoder)
enc.dataShards = dataShards
enc.parityShards = parityShards
enc.shardSize = dataShards + parityShards
enc.paws = 0xffffffff / uint32(enc.shardSize) * uint32(enc.shardSize)
enc.headerOffset = offset
enc.payloadOffset = enc.headerOffset + fecHeaderSize
codec, err := reedsolomon.New(dataShards, parityShards)
if err != nil {
return nil
}
enc.codec = codec
// caches
enc.encodeCache = make([][]byte, enc.shardSize)
enc.shardCache = make([][]byte, enc.shardSize)
for k := range enc.shardCache {
enc.shardCache[k] = make([]byte, mtuLimit)
}
enc.zeros = make([]byte, mtuLimit)
return enc
}
// encodes the packet, outputs parity shards if we have collected quorum datashards
// notice: the contents of 'ps' will be re-written in successive calling
func (enc *fecEncoder) encode(b []byte) (ps [][]byte) {
// The header format:
// | FEC SEQID(4B) | FEC TYPE(2B) | SIZE (2B) | PAYLOAD(SIZE-2) |
// |<-headerOffset |<-payloadOffset
enc.markData(b[enc.headerOffset:])
binary.LittleEndian.PutUint16(b[enc.payloadOffset:], uint16(len(b[enc.payloadOffset:])))
// copy data from payloadOffset to fec shard cache
sz := len(b)
enc.shardCache[enc.shardCount] = enc.shardCache[enc.shardCount][:sz]
copy(enc.shardCache[enc.shardCount][enc.payloadOffset:], b[enc.payloadOffset:])
enc.shardCount++
// track max datashard length
if sz > enc.maxSize {
enc.maxSize = sz
}
// Generation of Reed-Solomon Erasure Code
if enc.shardCount == enc.dataShards {
// fill '0' into the tail of each datashard
for i := 0; i < enc.dataShards; i++ {
shard := enc.shardCache[i]
slen := len(shard)
copy(shard[slen:enc.maxSize], enc.zeros)
}
// construct equal-sized slice with stripped header
cache := enc.encodeCache
for k := range cache {
cache[k] = enc.shardCache[k][enc.payloadOffset:enc.maxSize]
}
// encoding
if err := enc.codec.Encode(cache); err == nil {
ps = enc.shardCache[enc.dataShards:]
for k := range ps {
enc.markParity(ps[k][enc.headerOffset:])
ps[k] = ps[k][:enc.maxSize]
}
}
// counters resetting
enc.shardCount = 0
enc.maxSize = 0
}
return
}
func (enc *fecEncoder) markData(data []byte) {
binary.LittleEndian.PutUint32(data, enc.next)
binary.LittleEndian.PutUint16(data[4:], typeData)
enc.next++
}
func (enc *fecEncoder) markParity(data []byte) {
binary.LittleEndian.PutUint32(data, enc.next)
binary.LittleEndian.PutUint16(data[4:], typeParity)
// sequence wrap will only happen at parity shard
enc.next = (enc.next + 1) % enc.paws
}

43
gate-hk4e/kcp/fec_test.go Normal file
View File

@@ -0,0 +1,43 @@
package kcp
import (
"encoding/binary"
"math/rand"
"testing"
)
func BenchmarkFECDecode(b *testing.B) {
const dataSize = 10
const paritySize = 3
const payLoad = 1500
decoder := newFECDecoder(dataSize, paritySize)
b.ReportAllocs()
b.SetBytes(payLoad)
for i := 0; i < b.N; i++ {
if rand.Int()%(dataSize+paritySize) == 0 { // random loss
continue
}
pkt := make([]byte, payLoad)
binary.LittleEndian.PutUint32(pkt, uint32(i))
if i%(dataSize+paritySize) >= dataSize {
binary.LittleEndian.PutUint16(pkt[4:], typeParity)
} else {
binary.LittleEndian.PutUint16(pkt[4:], typeData)
}
decoder.decode(pkt)
}
}
func BenchmarkFECEncode(b *testing.B) {
const dataSize = 10
const paritySize = 3
const payLoad = 1500
b.ReportAllocs()
b.SetBytes(payLoad)
encoder := newFECEncoder(dataSize, paritySize, 0)
for i := 0; i < b.N; i++ {
data := make([]byte, payLoad)
encoder.encode(data)
}
}

1094
gate-hk4e/kcp/kcp.go Normal file

File diff suppressed because it is too large Load Diff

135
gate-hk4e/kcp/kcp_test.go Normal file
View File

@@ -0,0 +1,135 @@
package kcp
import (
"io"
"net"
"sync"
"testing"
"time"
"github.com/xtaci/lossyconn"
)
const repeat = 16
func TestLossyConn1(t *testing.T) {
t.Log("testing loss rate 10%, rtt 200ms")
t.Log("testing link with nodelay parameters:1 10 2 1")
client, err := lossyconn.NewLossyConn(0.1, 100)
if err != nil {
t.Fatal(err)
}
server, err := lossyconn.NewLossyConn(0.1, 100)
if err != nil {
t.Fatal(err)
}
testlink(t, client, server, 1, 10, 2, 1)
}
func TestLossyConn2(t *testing.T) {
t.Log("testing loss rate 20%, rtt 200ms")
t.Log("testing link with nodelay parameters:1 10 2 1")
client, err := lossyconn.NewLossyConn(0.2, 100)
if err != nil {
t.Fatal(err)
}
server, err := lossyconn.NewLossyConn(0.2, 100)
if err != nil {
t.Fatal(err)
}
testlink(t, client, server, 1, 10, 2, 1)
}
func TestLossyConn3(t *testing.T) {
t.Log("testing loss rate 30%, rtt 200ms")
t.Log("testing link with nodelay parameters:1 10 2 1")
client, err := lossyconn.NewLossyConn(0.3, 100)
if err != nil {
t.Fatal(err)
}
server, err := lossyconn.NewLossyConn(0.3, 100)
if err != nil {
t.Fatal(err)
}
testlink(t, client, server, 1, 10, 2, 1)
}
func TestLossyConn4(t *testing.T) {
t.Log("testing loss rate 10%, rtt 200ms")
t.Log("testing link with nodelay parameters:1 10 2 0")
client, err := lossyconn.NewLossyConn(0.1, 100)
if err != nil {
t.Fatal(err)
}
server, err := lossyconn.NewLossyConn(0.1, 100)
if err != nil {
t.Fatal(err)
}
testlink(t, client, server, 1, 10, 2, 0)
}
func testlink(t *testing.T, client *lossyconn.LossyConn, server *lossyconn.LossyConn, nodelay, interval, resend, nc int) {
t.Log("testing with nodelay parameters:", nodelay, interval, resend, nc)
sess, _ := NewConn2(server.LocalAddr(), nil, 0, 0, client)
listener, _ := ServeConn(nil, 0, 0, server)
echoServer := func(l *Listener) {
for {
conn, err := l.AcceptKCP()
if err != nil {
return
}
go func() {
conn.SetNoDelay(nodelay, interval, resend, nc)
buf := make([]byte, 65536)
for {
n, err := conn.Read(buf)
if err != nil {
return
}
conn.Write(buf[:n])
}
}()
}
}
echoTester := func(s *UDPSession, raddr net.Addr) {
s.SetNoDelay(nodelay, interval, resend, nc)
buf := make([]byte, 64)
var rtt time.Duration
for i := 0; i < repeat; i++ {
start := time.Now()
s.Write(buf)
io.ReadFull(s, buf)
rtt += time.Since(start)
}
t.Log("client:", client)
t.Log("server:", server)
t.Log("avg rtt:", rtt/repeat)
t.Logf("total time: %v for %v round trip:", rtt, repeat)
}
go echoServer(listener)
echoTester(sess, server.LocalAddr())
}
func BenchmarkFlush(b *testing.B) {
kcp := NewKCP(1, func(buf []byte, size int) {})
kcp.snd_buf = make([]segment, 1024)
for k := range kcp.snd_buf {
kcp.snd_buf[k].xmit = 1
kcp.snd_buf[k].resendts = currentMs() + 10000
}
b.ResetTimer()
b.ReportAllocs()
var mu sync.Mutex
for i := 0; i < b.N; i++ {
mu.Lock()
kcp.flush(false)
mu.Unlock()
}
}

126
gate-hk4e/kcp/readloop.go Normal file
View File

@@ -0,0 +1,126 @@
package kcp
import (
"bytes"
"encoding/binary"
"github.com/pkg/errors"
)
func (s *UDPSession) defaultReadLoop() {
buf := make([]byte, mtuLimit)
var src string
for {
if n, addr, err := s.conn.ReadFrom(buf); err == nil {
udpPayload := buf[:n]
// make sure the packet is from the same source
if src == "" { // set source address
src = addr.String()
} else if addr.String() != src {
//atomic.AddUint64(&DefaultSnmp.InErrs, 1)
//continue
s.remote = addr
src = addr.String()
}
s.packetInput(udpPayload)
} else {
s.notifyReadError(errors.WithStack(err))
return
}
}
}
func (l *Listener) defaultMonitor() {
buf := make([]byte, mtuLimit)
for {
if n, from, err := l.conn.ReadFrom(buf); err == nil {
udpPayload := buf[:n]
var convId uint64 = 0
if n == 20 {
// 原神KCP的Enet协议
// 提取convId
convId += uint64(udpPayload[4]) << 24
convId += uint64(udpPayload[5]) << 16
convId += uint64(udpPayload[6]) << 8
convId += uint64(udpPayload[7]) << 0
convId += uint64(udpPayload[8]) << 56
convId += uint64(udpPayload[9]) << 48
convId += uint64(udpPayload[10]) << 40
convId += uint64(udpPayload[11]) << 32
// 提取Enet协议头部和尾部幻数
udpPayloadEnetHead := udpPayload[:4]
udpPayloadEnetTail := udpPayload[len(udpPayload)-4:]
// 提取Enet协议类型
enetTypeData := udpPayload[12:16]
enetTypeDataBuffer := bytes.NewBuffer(enetTypeData)
var enetType uint32
_ = binary.Read(enetTypeDataBuffer, binary.BigEndian, &enetType)
equalHead := bytes.Compare(udpPayloadEnetHead, MagicEnetSynHead)
equalTail := bytes.Compare(udpPayloadEnetTail, MagicEnetSynTail)
if equalHead == 0 && equalTail == 0 {
// 客户端前置握手获取conv
l.EnetNotify <- &Enet{
Addr: from.String(),
ConvId: convId,
ConnType: ConnEnetSyn,
EnetType: enetType,
}
continue
}
equalHead = bytes.Compare(udpPayloadEnetHead, MagicEnetEstHead)
equalTail = bytes.Compare(udpPayloadEnetTail, MagicEnetEstTail)
if equalHead == 0 && equalTail == 0 {
// 连接建立
l.EnetNotify <- &Enet{
Addr: from.String(),
ConvId: convId,
ConnType: ConnEnetEst,
EnetType: enetType,
}
continue
}
equalHead = bytes.Compare(udpPayloadEnetHead, MagicEnetFinHead)
equalTail = bytes.Compare(udpPayloadEnetTail, MagicEnetFinTail)
if equalHead == 0 && equalTail == 0 {
// 连接断开
l.EnetNotify <- &Enet{
Addr: from.String(),
ConvId: convId,
ConnType: ConnEnetFin,
EnetType: enetType,
}
continue
}
} else {
// 正常KCP包
convId += uint64(udpPayload[0]) << 0
convId += uint64(udpPayload[1]) << 8
convId += uint64(udpPayload[2]) << 16
convId += uint64(udpPayload[3]) << 24
convId += uint64(udpPayload[4]) << 32
convId += uint64(udpPayload[5]) << 40
convId += uint64(udpPayload[6]) << 48
convId += uint64(udpPayload[7]) << 56
}
l.sessionLock.RLock()
conn, exist := l.sessions[convId]
l.sessionLock.RUnlock()
if exist {
if conn.remote.String() != from.String() {
conn.remote = from
// 连接地址改变
l.EnetNotify <- &Enet{
Addr: conn.remote.String(),
ConvId: convId,
ConnType: ConnEnetAddrChange,
}
}
}
l.packetInput(udpPayload, from, convId)
} else {
l.notifyReadError(errors.WithStack(err))
return
}
}
}

View File

@@ -0,0 +1,12 @@
//go:build !linux
// +build !linux
package kcp
func (s *UDPSession) readLoop() {
s.defaultReadLoop()
}
func (l *Listener) monitor() {
l.defaultMonitor()
}

View File

@@ -0,0 +1,199 @@
//go:build linux
// +build linux
package kcp
import (
"bytes"
"encoding/binary"
"github.com/pkg/errors"
"golang.org/x/net/ipv4"
"golang.org/x/net/ipv6"
"net"
"os"
)
// the read loop for a client session
func (s *UDPSession) readLoop() {
// default version
if s.xconn == nil {
s.defaultReadLoop()
return
}
// x/net version
var src string
msgs := make([]ipv4.Message, batchSize)
for k := range msgs {
msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
}
for {
if count, err := s.xconn.ReadBatch(msgs, 0); err == nil {
for i := 0; i < count; i++ {
msg := &msgs[i]
// make sure the packet is from the same source
if src == "" { // set source address if nil
src = msg.Addr.String()
} else if msg.Addr.String() != src {
//atomic.AddUint64(&DefaultSnmp.InErrs, 1)
//continue
s.remote = msg.Addr
src = msg.Addr.String()
}
udpPayload := msg.Buffers[0][:msg.N]
// source and size has validated
s.packetInput(udpPayload)
}
} else {
// compatibility issue:
// for linux kernel<=2.6.32, support for sendmmsg is not available
// an error of type os.SyscallError will be returned
if operr, ok := err.(*net.OpError); ok {
if se, ok := operr.Err.(*os.SyscallError); ok {
if se.Syscall == "recvmmsg" {
s.defaultReadLoop()
return
}
}
}
s.notifyReadError(errors.WithStack(err))
return
}
}
}
// monitor incoming data for all connections of server
func (l *Listener) monitor() {
var xconn batchConn
if _, ok := l.conn.(*net.UDPConn); ok {
addr, err := net.ResolveUDPAddr("udp", l.conn.LocalAddr().String())
if err == nil {
if addr.IP.To4() != nil {
xconn = ipv4.NewPacketConn(l.conn)
} else {
xconn = ipv6.NewPacketConn(l.conn)
}
}
}
// default version
if xconn == nil {
l.defaultMonitor()
return
}
// x/net version
msgs := make([]ipv4.Message, batchSize)
for k := range msgs {
msgs[k].Buffers = [][]byte{make([]byte, mtuLimit)}
}
for {
if count, err := xconn.ReadBatch(msgs, 0); err == nil {
for i := 0; i < count; i++ {
msg := &msgs[i]
udpPayload := msg.Buffers[0][:msg.N]
var convId uint64 = 0
if msg.N == 20 {
// 原神KCP的Enet协议
// 提取convId
convId += uint64(udpPayload[4]) << 24
convId += uint64(udpPayload[5]) << 16
convId += uint64(udpPayload[6]) << 8
convId += uint64(udpPayload[7]) << 0
convId += uint64(udpPayload[8]) << 56
convId += uint64(udpPayload[9]) << 48
convId += uint64(udpPayload[10]) << 40
convId += uint64(udpPayload[11]) << 32
// 提取Enet协议头部和尾部幻数
udpPayloadEnetHead := udpPayload[:4]
udpPayloadEnetTail := udpPayload[len(udpPayload)-4:]
// 提取Enet协议类型
enetTypeData := udpPayload[12:16]
enetTypeDataBuffer := bytes.NewBuffer(enetTypeData)
var enetType uint32
_ = binary.Read(enetTypeDataBuffer, binary.BigEndian, &enetType)
equalHead := bytes.Compare(udpPayloadEnetHead, MagicEnetSynHead)
equalTail := bytes.Compare(udpPayloadEnetTail, MagicEnetSynTail)
if equalHead == 0 && equalTail == 0 {
// 客户端前置握手获取conv
l.EnetNotify <- &Enet{
Addr: msg.Addr.String(),
ConvId: convId,
ConnType: ConnEnetSyn,
EnetType: enetType,
}
continue
}
equalHead = bytes.Compare(udpPayloadEnetHead, MagicEnetEstHead)
equalTail = bytes.Compare(udpPayloadEnetTail, MagicEnetEstTail)
if equalHead == 0 && equalTail == 0 {
// 连接建立
l.EnetNotify <- &Enet{
Addr: msg.Addr.String(),
ConvId: convId,
ConnType: ConnEnetEst,
EnetType: enetType,
}
continue
}
equalHead = bytes.Compare(udpPayloadEnetHead, MagicEnetFinHead)
equalTail = bytes.Compare(udpPayloadEnetTail, MagicEnetFinTail)
if equalHead == 0 && equalTail == 0 {
// 连接断开
l.EnetNotify <- &Enet{
Addr: msg.Addr.String(),
ConvId: convId,
ConnType: ConnEnetFin,
EnetType: enetType,
}
continue
}
} else {
// 正常KCP包
convId += uint64(udpPayload[0]) << 0
convId += uint64(udpPayload[1]) << 8
convId += uint64(udpPayload[2]) << 16
convId += uint64(udpPayload[3]) << 24
convId += uint64(udpPayload[4]) << 32
convId += uint64(udpPayload[5]) << 40
convId += uint64(udpPayload[6]) << 48
convId += uint64(udpPayload[7]) << 56
}
l.sessionLock.RLock()
conn, exist := l.sessions[convId]
l.sessionLock.RUnlock()
if exist {
if conn.remote.String() != msg.Addr.String() {
conn.remote = msg.Addr
// 连接地址改变
l.EnetNotify <- &Enet{
Addr: conn.remote.String(),
ConvId: convId,
ConnType: ConnEnetAddrChange,
}
}
}
l.packetInput(udpPayload, msg.Addr, convId)
}
} else {
// compatibility issue:
// for linux kernel<=2.6.32, support for sendmmsg is not available
// an error of type os.SyscallError will be returned
if operr, ok := err.(*net.OpError); ok {
if se, ok := operr.Err.(*os.SyscallError); ok {
if se.Syscall == "recvmmsg" {
l.defaultMonitor()
return
}
}
}
l.notifyReadError(errors.WithStack(err))
return
}
}
}

1144
gate-hk4e/kcp/sess.go Normal file

File diff suppressed because it is too large Load Diff

703
gate-hk4e/kcp/sess_test.go Normal file
View File

@@ -0,0 +1,703 @@
package kcp
import (
"crypto/sha1"
"fmt"
"io"
"log"
"net"
"net/http"
_ "net/http/pprof"
"sync"
"sync/atomic"
"testing"
"time"
"golang.org/x/crypto/pbkdf2"
)
var baseport = uint32(10000)
var key = []byte("testkey")
var pass = pbkdf2.Key(key, []byte("testsalt"), 4096, 32, sha1.New)
func init() {
go func() {
log.Println(http.ListenAndServe("0.0.0.0:6060", nil))
}()
log.Println("beginning tests, encryption:salsa20, fec:10/3")
}
func dialEcho(port int) (*UDPSession, error) {
//block, _ := NewNoneBlockCrypt(pass)
//block, _ := NewSimpleXORBlockCrypt(pass)
//block, _ := NewTEABlockCrypt(pass[:16])
//block, _ := NewAESBlockCrypt(pass)
block, _ := NewSalsa20BlockCrypt(pass)
sess, err := DialWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 3)
if err != nil {
panic(err)
}
sess.SetStreamMode(true)
sess.SetStreamMode(false)
sess.SetStreamMode(true)
sess.SetWindowSize(1024, 1024)
sess.SetReadBuffer(16 * 1024 * 1024)
sess.SetWriteBuffer(16 * 1024 * 1024)
sess.SetStreamMode(true)
sess.SetNoDelay(1, 10, 2, 1)
sess.SetMtu(1400)
sess.SetMtu(1600)
sess.SetMtu(1400)
sess.SetACKNoDelay(true)
sess.SetACKNoDelay(false)
sess.SetDeadline(time.Now().Add(time.Minute))
return sess, err
}
func dialSink(port int) (*UDPSession, error) {
sess, err := DialWithOptions(fmt.Sprintf("127.0.0.1:%v", port), nil, 0, 0)
if err != nil {
panic(err)
}
sess.SetStreamMode(true)
sess.SetWindowSize(1024, 1024)
sess.SetReadBuffer(16 * 1024 * 1024)
sess.SetWriteBuffer(16 * 1024 * 1024)
sess.SetStreamMode(true)
sess.SetNoDelay(1, 10, 2, 1)
sess.SetMtu(1400)
sess.SetACKNoDelay(false)
sess.SetDeadline(time.Now().Add(time.Minute))
return sess, err
}
func dialTinyBufferEcho(port int) (*UDPSession, error) {
//block, _ := NewNoneBlockCrypt(pass)
//block, _ := NewSimpleXORBlockCrypt(pass)
//block, _ := NewTEABlockCrypt(pass[:16])
//block, _ := NewAESBlockCrypt(pass)
block, _ := NewSalsa20BlockCrypt(pass)
sess, err := DialWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 3)
if err != nil {
panic(err)
}
return sess, err
}
// ////////////////////////
func listenEcho(port int) (net.Listener, error) {
//block, _ := NewNoneBlockCrypt(pass)
//block, _ := NewSimpleXORBlockCrypt(pass)
//block, _ := NewTEABlockCrypt(pass[:16])
//block, _ := NewAESBlockCrypt(pass)
block, _ := NewSalsa20BlockCrypt(pass)
return ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 0)
}
func listenTinyBufferEcho(port int) (net.Listener, error) {
//block, _ := NewNoneBlockCrypt(pass)
//block, _ := NewSimpleXORBlockCrypt(pass)
//block, _ := NewTEABlockCrypt(pass[:16])
//block, _ := NewAESBlockCrypt(pass)
block, _ := NewSalsa20BlockCrypt(pass)
return ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), block, 10, 3)
}
func listenSink(port int) (net.Listener, error) {
return ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), nil, 0, 0)
}
func echoServer(port int) net.Listener {
l, err := listenEcho(port)
if err != nil {
panic(err)
}
go func() {
kcplistener := l.(*Listener)
kcplistener.SetReadBuffer(4 * 1024 * 1024)
kcplistener.SetWriteBuffer(4 * 1024 * 1024)
kcplistener.SetDSCP(46)
for {
s, err := l.Accept()
if err != nil {
return
}
// coverage test
s.(*UDPSession).SetReadBuffer(4 * 1024 * 1024)
s.(*UDPSession).SetWriteBuffer(4 * 1024 * 1024)
go handleEcho(s.(*UDPSession))
}
}()
return l
}
func sinkServer(port int) net.Listener {
l, err := listenSink(port)
if err != nil {
panic(err)
}
go func() {
kcplistener := l.(*Listener)
kcplistener.SetReadBuffer(4 * 1024 * 1024)
kcplistener.SetWriteBuffer(4 * 1024 * 1024)
kcplistener.SetDSCP(46)
for {
s, err := l.Accept()
if err != nil {
return
}
go handleSink(s.(*UDPSession))
}
}()
return l
}
func tinyBufferEchoServer(port int) net.Listener {
l, err := listenTinyBufferEcho(port)
if err != nil {
panic(err)
}
go func() {
for {
s, err := l.Accept()
if err != nil {
return
}
go handleTinyBufferEcho(s.(*UDPSession))
}
}()
return l
}
///////////////////////////
func handleEcho(conn *UDPSession) {
conn.SetStreamMode(true)
conn.SetWindowSize(4096, 4096)
conn.SetNoDelay(1, 10, 2, 1)
conn.SetDSCP(46)
conn.SetMtu(1400)
conn.SetACKNoDelay(false)
conn.SetReadDeadline(time.Now().Add(time.Hour))
conn.SetWriteDeadline(time.Now().Add(time.Hour))
buf := make([]byte, 65536)
for {
n, err := conn.Read(buf)
if err != nil {
return
}
conn.Write(buf[:n])
}
}
func handleSink(conn *UDPSession) {
conn.SetStreamMode(true)
conn.SetWindowSize(4096, 4096)
conn.SetNoDelay(1, 10, 2, 1)
conn.SetDSCP(46)
conn.SetMtu(1400)
conn.SetACKNoDelay(false)
conn.SetReadDeadline(time.Now().Add(time.Hour))
conn.SetWriteDeadline(time.Now().Add(time.Hour))
buf := make([]byte, 65536)
for {
_, err := conn.Read(buf)
if err != nil {
return
}
}
}
func handleTinyBufferEcho(conn *UDPSession) {
conn.SetStreamMode(true)
buf := make([]byte, 2)
for {
n, err := conn.Read(buf)
if err != nil {
return
}
conn.Write(buf[:n])
}
}
///////////////////////////
func TestTimeout(t *testing.T) {
port := int(atomic.AddUint32(&baseport, 1))
l := echoServer(port)
defer l.Close()
cli, err := dialEcho(port)
if err != nil {
panic(err)
}
buf := make([]byte, 10)
//timeout
cli.SetDeadline(time.Now().Add(time.Second))
<-time.After(2 * time.Second)
n, err := cli.Read(buf)
if n != 0 || err == nil {
t.Fail()
}
cli.Close()
}
func TestSendRecv(t *testing.T) {
port := int(atomic.AddUint32(&baseport, 1))
l := echoServer(port)
defer l.Close()
cli, err := dialEcho(port)
if err != nil {
panic(err)
}
cli.SetWriteDelay(true)
cli.SetDUP(1)
const N = 100
buf := make([]byte, 10)
for i := 0; i < N; i++ {
msg := fmt.Sprintf("hello%v", i)
cli.Write([]byte(msg))
if n, err := cli.Read(buf); err == nil {
if string(buf[:n]) != msg {
t.Fail()
}
} else {
panic(err)
}
}
cli.Close()
}
func TestSendVector(t *testing.T) {
port := int(atomic.AddUint32(&baseport, 1))
l := echoServer(port)
defer l.Close()
cli, err := dialEcho(port)
if err != nil {
panic(err)
}
cli.SetWriteDelay(false)
const N = 100
buf := make([]byte, 20)
v := make([][]byte, 2)
for i := 0; i < N; i++ {
v[0] = []byte(fmt.Sprintf("hello%v", i))
v[1] = []byte(fmt.Sprintf("world%v", i))
msg := fmt.Sprintf("hello%vworld%v", i, i)
cli.WriteBuffers(v)
if n, err := cli.Read(buf); err == nil {
if string(buf[:n]) != msg {
t.Error(string(buf[:n]), msg)
}
} else {
panic(err)
}
}
cli.Close()
}
func TestTinyBufferReceiver(t *testing.T) {
port := int(atomic.AddUint32(&baseport, 1))
l := tinyBufferEchoServer(port)
defer l.Close()
cli, err := dialTinyBufferEcho(port)
if err != nil {
panic(err)
}
const N = 100
snd := byte(0)
fillBuffer := func(buf []byte) {
for i := 0; i < len(buf); i++ {
buf[i] = snd
snd++
}
}
rcv := byte(0)
check := func(buf []byte) bool {
for i := 0; i < len(buf); i++ {
if buf[i] != rcv {
return false
}
rcv++
}
return true
}
sndbuf := make([]byte, 7)
rcvbuf := make([]byte, 7)
for i := 0; i < N; i++ {
fillBuffer(sndbuf)
cli.Write(sndbuf)
if n, err := io.ReadFull(cli, rcvbuf); err == nil {
if !check(rcvbuf[:n]) {
t.Fail()
}
} else {
panic(err)
}
}
cli.Close()
}
func TestClose(t *testing.T) {
var n int
var err error
port := int(atomic.AddUint32(&baseport, 1))
l := echoServer(port)
defer l.Close()
cli, err := dialEcho(port)
if err != nil {
panic(err)
}
// double close
cli.Close()
if cli.Close() == nil {
t.Fatal("double close misbehavior")
}
// write after close
buf := make([]byte, 10)
n, err = cli.Write(buf)
if n != 0 || err == nil {
t.Fatal("write after close misbehavior")
}
// write, close, read, read
cli, err = dialEcho(port)
if err != nil {
panic(err)
}
if n, err = cli.Write(buf); err != nil {
t.Fatal("write misbehavior")
}
// wait until data arrival
time.Sleep(2 * time.Second)
// drain
cli.Close()
n, err = io.ReadFull(cli, buf)
if err != nil {
t.Fatal("closed conn drain bytes failed", err, n)
}
// after drain, read should return error
n, err = cli.Read(buf)
if n != 0 || err == nil {
t.Fatal("write->close->drain->read misbehavior", err, n)
}
cli.Close()
}
func TestParallel1024CLIENT_64BMSG_64CNT(t *testing.T) {
port := int(atomic.AddUint32(&baseport, 1))
l := echoServer(port)
defer l.Close()
var wg sync.WaitGroup
wg.Add(1024)
for i := 0; i < 1024; i++ {
go parallel_client(&wg, port)
}
wg.Wait()
}
func parallel_client(wg *sync.WaitGroup, port int) (err error) {
cli, err := dialEcho(port)
if err != nil {
panic(err)
}
err = echo_tester(cli, 64, 64)
cli.Close()
wg.Done()
return
}
func BenchmarkEchoSpeed4K(b *testing.B) {
speedclient(b, 4096)
}
func BenchmarkEchoSpeed64K(b *testing.B) {
speedclient(b, 65536)
}
func BenchmarkEchoSpeed512K(b *testing.B) {
speedclient(b, 524288)
}
func BenchmarkEchoSpeed1M(b *testing.B) {
speedclient(b, 1048576)
}
func speedclient(b *testing.B, nbytes int) {
port := int(atomic.AddUint32(&baseport, 1))
l := echoServer(port)
defer l.Close()
b.ReportAllocs()
cli, err := dialEcho(port)
if err != nil {
panic(err)
}
if err := echo_tester(cli, nbytes, b.N); err != nil {
b.Fail()
}
b.SetBytes(int64(nbytes))
cli.Close()
}
func BenchmarkSinkSpeed4K(b *testing.B) {
sinkclient(b, 4096)
}
func BenchmarkSinkSpeed64K(b *testing.B) {
sinkclient(b, 65536)
}
func BenchmarkSinkSpeed256K(b *testing.B) {
sinkclient(b, 524288)
}
func BenchmarkSinkSpeed1M(b *testing.B) {
sinkclient(b, 1048576)
}
func sinkclient(b *testing.B, nbytes int) {
port := int(atomic.AddUint32(&baseport, 1))
l := sinkServer(port)
defer l.Close()
b.ReportAllocs()
cli, err := dialSink(port)
if err != nil {
panic(err)
}
sink_tester(cli, nbytes, b.N)
b.SetBytes(int64(nbytes))
cli.Close()
}
func echo_tester(cli net.Conn, msglen, msgcount int) error {
buf := make([]byte, msglen)
for i := 0; i < msgcount; i++ {
// send packet
if _, err := cli.Write(buf); err != nil {
return err
}
// receive packet
nrecv := 0
for {
n, err := cli.Read(buf)
if err != nil {
return err
} else {
nrecv += n
if nrecv == msglen {
break
}
}
}
}
return nil
}
func sink_tester(cli *UDPSession, msglen, msgcount int) error {
// sender
buf := make([]byte, msglen)
for i := 0; i < msgcount; i++ {
if _, err := cli.Write(buf); err != nil {
return err
}
}
return nil
}
func TestSNMP(t *testing.T) {
t.Log(DefaultSnmp.Copy())
t.Log(DefaultSnmp.Header())
t.Log(DefaultSnmp.ToSlice())
DefaultSnmp.Reset()
t.Log(DefaultSnmp.ToSlice())
}
func TestListenerClose(t *testing.T) {
port := int(atomic.AddUint32(&baseport, 1))
l, err := ListenWithOptions(fmt.Sprintf("127.0.0.1:%v", port), nil, 10, 3)
if err != nil {
t.Fail()
}
l.SetReadDeadline(time.Now().Add(time.Second))
l.SetWriteDeadline(time.Now().Add(time.Second))
l.SetDeadline(time.Now().Add(time.Second))
time.Sleep(2 * time.Second)
if _, err := l.Accept(); err == nil {
t.Fail()
}
l.Close()
//fakeaddr, _ := net.ResolveUDPAddr("udp6", "127.0.0.1:1111")
fakeConvId := uint64(0)
if l.closeSession(fakeConvId) {
t.Fail()
}
}
// A wrapper for net.PacketConn that remembers when Close has been called.
type closedFlagPacketConn struct {
net.PacketConn
Closed bool
}
func (c *closedFlagPacketConn) Close() error {
c.Closed = true
return c.PacketConn.Close()
}
func newClosedFlagPacketConn(c net.PacketConn) *closedFlagPacketConn {
return &closedFlagPacketConn{c, false}
}
// Listener should close a net.PacketConn that it created.
// https://github.com/xtaci/kcp-go/issues/165
func TestListenerOwnedPacketConn(t *testing.T) {
// ListenWithOptions creates its own net.PacketConn.
l, err := ListenWithOptions("127.0.0.1:0", nil, 0, 0)
if err != nil {
panic(err)
}
defer l.Close()
// Replace the internal net.PacketConn with one that remembers when it
// has been closed.
pconn := newClosedFlagPacketConn(l.conn)
l.conn = pconn
if pconn.Closed {
t.Fatal("owned PacketConn closed before Listener.Close()")
}
err = l.Close()
if err != nil {
panic(err)
}
if !pconn.Closed {
t.Fatal("owned PacketConn not closed after Listener.Close()")
}
}
// Listener should not close a net.PacketConn that it did not create.
// https://github.com/xtaci/kcp-go/issues/165
func TestListenerNonOwnedPacketConn(t *testing.T) {
// Create a net.PacketConn not owned by the Listener.
c, err := net.ListenPacket("udp", "127.0.0.1:0")
if err != nil {
panic(err)
}
defer c.Close()
// Make it remember when it has been closed.
pconn := newClosedFlagPacketConn(c)
l, err := ServeConn(nil, 0, 0, pconn)
if err != nil {
panic(err)
}
defer l.Close()
if pconn.Closed {
t.Fatal("non-owned PacketConn closed before Listener.Close()")
}
err = l.Close()
if err != nil {
panic(err)
}
if pconn.Closed {
t.Fatal("non-owned PacketConn closed after Listener.Close()")
}
}
// UDPSession should close a net.PacketConn that it created.
// https://github.com/xtaci/kcp-go/issues/165
func TestUDPSessionOwnedPacketConn(t *testing.T) {
l := sinkServer(0)
defer l.Close()
// DialWithOptions creates its own net.PacketConn.
client, err := DialWithOptions(l.Addr().String(), nil, 0, 0)
if err != nil {
panic(err)
}
defer client.Close()
// Replace the internal net.PacketConn with one that remembers when it
// has been closed.
pconn := newClosedFlagPacketConn(client.conn)
client.conn = pconn
if pconn.Closed {
t.Fatal("owned PacketConn closed before UDPSession.Close()")
}
err = client.Close()
if err != nil {
panic(err)
}
if !pconn.Closed {
t.Fatal("owned PacketConn not closed after UDPSession.Close()")
}
}
// UDPSession should not close a net.PacketConn that it did not create.
// https://github.com/xtaci/kcp-go/issues/165
func TestUDPSessionNonOwnedPacketConn(t *testing.T) {
l := sinkServer(0)
defer l.Close()
// Create a net.PacketConn not owned by the UDPSession.
c, err := net.ListenPacket("udp", "127.0.0.1:0")
if err != nil {
panic(err)
}
defer c.Close()
// Make it remember when it has been closed.
pconn := newClosedFlagPacketConn(c)
client, err := NewConn2(l.Addr(), nil, 0, 0, pconn)
if err != nil {
panic(err)
}
defer client.Close()
if pconn.Closed {
t.Fatal("non-owned PacketConn closed before UDPSession.Close()")
}
err = client.Close()
if err != nil {
panic(err)
}
if pconn.Closed {
t.Fatal("non-owned PacketConn closed after UDPSession.Close()")
}
}

164
gate-hk4e/kcp/snmp.go Normal file
View File

@@ -0,0 +1,164 @@
package kcp
import (
"fmt"
"sync/atomic"
)
// Snmp defines network statistics indicator
type Snmp struct {
BytesSent uint64 // bytes sent from upper level
BytesReceived uint64 // bytes received to upper level
MaxConn uint64 // max number of connections ever reached
ActiveOpens uint64 // accumulated active open connections
PassiveOpens uint64 // accumulated passive open connections
CurrEstab uint64 // current number of established connections
InErrs uint64 // UDP read errors reported from net.PacketConn
InCsumErrors uint64 // checksum errors from CRC32
KCPInErrors uint64 // packet iput errors reported from KCP
InPkts uint64 // incoming packets count
OutPkts uint64 // outgoing packets count
InSegs uint64 // incoming KCP segments
OutSegs uint64 // outgoing KCP segments
InBytes uint64 // UDP bytes received
OutBytes uint64 // UDP bytes sent
RetransSegs uint64 // accmulated retransmited segments
FastRetransSegs uint64 // accmulated fast retransmitted segments
EarlyRetransSegs uint64 // accmulated early retransmitted segments
LostSegs uint64 // number of segs inferred as lost
RepeatSegs uint64 // number of segs duplicated
FECRecovered uint64 // correct packets recovered from FEC
FECErrs uint64 // incorrect packets recovered from FEC
FECParityShards uint64 // FEC segments received
FECShortShards uint64 // number of data shards that's not enough for recovery
}
func newSnmp() *Snmp {
return new(Snmp)
}
// Header returns all field names
func (s *Snmp) Header() []string {
return []string{
"BytesSent",
"BytesReceived",
"MaxConn",
"ActiveOpens",
"PassiveOpens",
"CurrEstab",
"InErrs",
"InCsumErrors",
"KCPInErrors",
"InPkts",
"OutPkts",
"InSegs",
"OutSegs",
"InBytes",
"OutBytes",
"RetransSegs",
"FastRetransSegs",
"EarlyRetransSegs",
"LostSegs",
"RepeatSegs",
"FECParityShards",
"FECErrs",
"FECRecovered",
"FECShortShards",
}
}
// ToSlice returns current snmp info as slice
func (s *Snmp) ToSlice() []string {
snmp := s.Copy()
return []string{
fmt.Sprint(snmp.BytesSent),
fmt.Sprint(snmp.BytesReceived),
fmt.Sprint(snmp.MaxConn),
fmt.Sprint(snmp.ActiveOpens),
fmt.Sprint(snmp.PassiveOpens),
fmt.Sprint(snmp.CurrEstab),
fmt.Sprint(snmp.InErrs),
fmt.Sprint(snmp.InCsumErrors),
fmt.Sprint(snmp.KCPInErrors),
fmt.Sprint(snmp.InPkts),
fmt.Sprint(snmp.OutPkts),
fmt.Sprint(snmp.InSegs),
fmt.Sprint(snmp.OutSegs),
fmt.Sprint(snmp.InBytes),
fmt.Sprint(snmp.OutBytes),
fmt.Sprint(snmp.RetransSegs),
fmt.Sprint(snmp.FastRetransSegs),
fmt.Sprint(snmp.EarlyRetransSegs),
fmt.Sprint(snmp.LostSegs),
fmt.Sprint(snmp.RepeatSegs),
fmt.Sprint(snmp.FECParityShards),
fmt.Sprint(snmp.FECErrs),
fmt.Sprint(snmp.FECRecovered),
fmt.Sprint(snmp.FECShortShards),
}
}
// Copy make a copy of current snmp snapshot
func (s *Snmp) Copy() *Snmp {
d := newSnmp()
d.BytesSent = atomic.LoadUint64(&s.BytesSent)
d.BytesReceived = atomic.LoadUint64(&s.BytesReceived)
d.MaxConn = atomic.LoadUint64(&s.MaxConn)
d.ActiveOpens = atomic.LoadUint64(&s.ActiveOpens)
d.PassiveOpens = atomic.LoadUint64(&s.PassiveOpens)
d.CurrEstab = atomic.LoadUint64(&s.CurrEstab)
d.InErrs = atomic.LoadUint64(&s.InErrs)
d.InCsumErrors = atomic.LoadUint64(&s.InCsumErrors)
d.KCPInErrors = atomic.LoadUint64(&s.KCPInErrors)
d.InPkts = atomic.LoadUint64(&s.InPkts)
d.OutPkts = atomic.LoadUint64(&s.OutPkts)
d.InSegs = atomic.LoadUint64(&s.InSegs)
d.OutSegs = atomic.LoadUint64(&s.OutSegs)
d.InBytes = atomic.LoadUint64(&s.InBytes)
d.OutBytes = atomic.LoadUint64(&s.OutBytes)
d.RetransSegs = atomic.LoadUint64(&s.RetransSegs)
d.FastRetransSegs = atomic.LoadUint64(&s.FastRetransSegs)
d.EarlyRetransSegs = atomic.LoadUint64(&s.EarlyRetransSegs)
d.LostSegs = atomic.LoadUint64(&s.LostSegs)
d.RepeatSegs = atomic.LoadUint64(&s.RepeatSegs)
d.FECParityShards = atomic.LoadUint64(&s.FECParityShards)
d.FECErrs = atomic.LoadUint64(&s.FECErrs)
d.FECRecovered = atomic.LoadUint64(&s.FECRecovered)
d.FECShortShards = atomic.LoadUint64(&s.FECShortShards)
return d
}
// Reset values to zero
func (s *Snmp) Reset() {
atomic.StoreUint64(&s.BytesSent, 0)
atomic.StoreUint64(&s.BytesReceived, 0)
atomic.StoreUint64(&s.MaxConn, 0)
atomic.StoreUint64(&s.ActiveOpens, 0)
atomic.StoreUint64(&s.PassiveOpens, 0)
atomic.StoreUint64(&s.CurrEstab, 0)
atomic.StoreUint64(&s.InErrs, 0)
atomic.StoreUint64(&s.InCsumErrors, 0)
atomic.StoreUint64(&s.KCPInErrors, 0)
atomic.StoreUint64(&s.InPkts, 0)
atomic.StoreUint64(&s.OutPkts, 0)
atomic.StoreUint64(&s.InSegs, 0)
atomic.StoreUint64(&s.OutSegs, 0)
atomic.StoreUint64(&s.InBytes, 0)
atomic.StoreUint64(&s.OutBytes, 0)
atomic.StoreUint64(&s.RetransSegs, 0)
atomic.StoreUint64(&s.FastRetransSegs, 0)
atomic.StoreUint64(&s.EarlyRetransSegs, 0)
atomic.StoreUint64(&s.LostSegs, 0)
atomic.StoreUint64(&s.RepeatSegs, 0)
atomic.StoreUint64(&s.FECParityShards, 0)
atomic.StoreUint64(&s.FECErrs, 0)
atomic.StoreUint64(&s.FECRecovered, 0)
atomic.StoreUint64(&s.FECShortShards, 0)
}
// DefaultSnmp is the global KCP connection statistics collector
var DefaultSnmp *Snmp
func init() {
DefaultSnmp = newSnmp()
}

146
gate-hk4e/kcp/timedsched.go Normal file
View File

@@ -0,0 +1,146 @@
package kcp
import (
"container/heap"
"runtime"
"sync"
"time"
)
// SystemTimedSched is the library level timed-scheduler
var SystemTimedSched = NewTimedSched(runtime.NumCPU())
type timedFunc struct {
execute func()
ts time.Time
}
// a heap for sorted timed function
type timedFuncHeap []timedFunc
func (h timedFuncHeap) Len() int { return len(h) }
func (h timedFuncHeap) Less(i, j int) bool { return h[i].ts.Before(h[j].ts) }
func (h timedFuncHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *timedFuncHeap) Push(x interface{}) { *h = append(*h, x.(timedFunc)) }
func (h *timedFuncHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
old[n-1].execute = nil // avoid memory leak
*h = old[0 : n-1]
return x
}
// TimedSched represents the control struct for timed parallel scheduler
type TimedSched struct {
// prepending tasks
prependTasks []timedFunc
prependLock sync.Mutex
chPrependNotify chan struct{}
// tasks will be distributed through chTask
chTask chan timedFunc
dieOnce sync.Once
die chan struct{}
}
// NewTimedSched creates a parallel-scheduler with given parallelization
func NewTimedSched(parallel int) *TimedSched {
ts := new(TimedSched)
ts.chTask = make(chan timedFunc)
ts.die = make(chan struct{})
ts.chPrependNotify = make(chan struct{}, 1)
for i := 0; i < parallel; i++ {
go ts.sched()
}
go ts.prepend()
return ts
}
func (ts *TimedSched) sched() {
var tasks timedFuncHeap
timer := time.NewTimer(0)
drained := false
for {
select {
case task := <-ts.chTask:
now := time.Now()
if now.After(task.ts) {
// already delayed! execute immediately
task.execute()
} else {
heap.Push(&tasks, task)
// properly reset timer to trigger based on the top element
stopped := timer.Stop()
if !stopped && !drained {
<-timer.C
}
timer.Reset(tasks[0].ts.Sub(now))
drained = false
}
case now := <-timer.C:
drained = true
for tasks.Len() > 0 {
if now.After(tasks[0].ts) {
heap.Pop(&tasks).(timedFunc).execute()
} else {
timer.Reset(tasks[0].ts.Sub(now))
drained = false
break
}
}
case <-ts.die:
return
}
}
}
func (ts *TimedSched) prepend() {
var tasks []timedFunc
for {
select {
case <-ts.chPrependNotify:
ts.prependLock.Lock()
// keep cap to reuse slice
if cap(tasks) < cap(ts.prependTasks) {
tasks = make([]timedFunc, 0, cap(ts.prependTasks))
}
tasks = tasks[:len(ts.prependTasks)]
copy(tasks, ts.prependTasks)
for k := range ts.prependTasks {
ts.prependTasks[k].execute = nil // avoid memory leak
}
ts.prependTasks = ts.prependTasks[:0]
ts.prependLock.Unlock()
for k := range tasks {
select {
case ts.chTask <- tasks[k]:
tasks[k].execute = nil // avoid memory leak
case <-ts.die:
return
}
}
tasks = tasks[:0]
case <-ts.die:
return
}
}
}
// Put a function 'f' awaiting to be executed at 'deadline'
func (ts *TimedSched) Put(f func(), deadline time.Time) {
ts.prependLock.Lock()
ts.prependTasks = append(ts.prependTasks, timedFunc{f, deadline})
ts.prependLock.Unlock()
select {
case ts.chPrependNotify <- struct{}{}:
default:
}
}
// Close terminates this scheduler
func (ts *TimedSched) Close() { ts.dieOnce.Do(func() { close(ts.die) }) }

80
gate-hk4e/kcp/tx.go Normal file
View File

@@ -0,0 +1,80 @@
package kcp
import (
"net"
"sync/atomic"
"github.com/pkg/errors"
"golang.org/x/net/ipv4"
)
func buildEnet(connType uint8, enetType uint32, conv uint64) []byte {
data := make([]byte, 20)
if connType == ConnEnetSyn {
copy(data[0:4], MagicEnetSynHead)
copy(data[16:20], MagicEnetSynTail)
} else if connType == ConnEnetEst {
copy(data[0:4], MagicEnetEstHead)
copy(data[16:20], MagicEnetEstTail)
} else if connType == ConnEnetFin {
copy(data[0:4], MagicEnetFinHead)
copy(data[16:20], MagicEnetFinTail)
} else {
return nil
}
// conv的高四个字节和低四个字节分开
// 例如 00 00 01 45 | LL LL LL LL | HH HH HH HH | 49 96 02 d2 | 14 51 45 45
data[4] = uint8(conv >> 24)
data[5] = uint8(conv >> 16)
data[6] = uint8(conv >> 8)
data[7] = uint8(conv >> 0)
data[8] = uint8(conv >> 56)
data[9] = uint8(conv >> 48)
data[10] = uint8(conv >> 40)
data[11] = uint8(conv >> 32)
// Enet
data[12] = uint8(enetType >> 24)
data[13] = uint8(enetType >> 16)
data[14] = uint8(enetType >> 8)
data[15] = uint8(enetType >> 0)
return data
}
func (l *Listener) defaultSendEnetNotifyToClient(enet *Enet) {
remoteAddr, err := net.ResolveUDPAddr("udp", enet.Addr)
if err != nil {
return
}
data := buildEnet(enet.ConnType, enet.EnetType, enet.ConvId)
if data == nil {
return
}
_, _ = l.conn.WriteTo(data, remoteAddr)
}
func (s *UDPSession) defaultSendEnetNotify(enet *Enet) {
data := buildEnet(enet.ConnType, enet.EnetType, s.GetConv())
if data == nil {
return
}
s.defaultTx([]ipv4.Message{{
Buffers: [][]byte{data},
Addr: s.remote,
}})
}
func (s *UDPSession) defaultTx(txqueue []ipv4.Message) {
nbytes := 0
npkts := 0
for k := range txqueue {
if n, err := s.conn.WriteTo(txqueue[k].Buffers[0], txqueue[k].Addr); err == nil {
nbytes += n
npkts++
} else {
s.notifyWriteError(errors.WithStack(err))
break
}
}
atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(npkts))
atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
}

View File

@@ -0,0 +1,20 @@
//go:build !linux
// +build !linux
package kcp
import (
"golang.org/x/net/ipv4"
)
func (l *Listener) SendEnetNotifyToClient(enet *Enet) {
l.defaultSendEnetNotifyToClient(enet)
}
func (s *UDPSession) SendEnetNotify(enet *Enet) {
s.defaultSendEnetNotify(enet)
}
func (s *UDPSession) tx(txqueue []ipv4.Message) {
s.defaultTx(txqueue)
}

102
gate-hk4e/kcp/tx_linux.go Normal file
View File

@@ -0,0 +1,102 @@
//go:build linux
// +build linux
package kcp
import (
"golang.org/x/net/ipv6"
"net"
"os"
"sync/atomic"
"github.com/pkg/errors"
"golang.org/x/net/ipv4"
)
func (l *Listener) SendEnetNotifyToClient(enet *Enet) {
var xconn batchConn
_, ok := l.conn.(*net.UDPConn)
if !ok {
return
}
localAddr, err := net.ResolveUDPAddr("udp", l.conn.LocalAddr().String())
if err != nil {
return
}
if localAddr.IP.To4() != nil {
xconn = ipv4.NewPacketConn(l.conn)
} else {
xconn = ipv6.NewPacketConn(l.conn)
}
// default version
if xconn == nil {
l.defaultSendEnetNotifyToClient(enet)
return
}
remoteAddr, err := net.ResolveUDPAddr("udp", enet.Addr)
if err != nil {
return
}
data := buildEnet(enet.ConnType, enet.EnetType, enet.ConvId)
if data == nil {
return
}
_, _ = xconn.WriteBatch([]ipv4.Message{{
Buffers: [][]byte{data},
Addr: remoteAddr,
}}, 0)
}
func (s *UDPSession) SendEnetNotify(enet *Enet) {
data := buildEnet(enet.ConnType, enet.EnetType, s.GetConv())
if data == nil {
return
}
s.tx([]ipv4.Message{{
Buffers: [][]byte{data},
Addr: s.remote,
}})
}
func (s *UDPSession) tx(txqueue []ipv4.Message) {
// default version
if s.xconn == nil || s.xconnWriteError != nil {
s.defaultTx(txqueue)
return
}
// x/net version
nbytes := 0
npkts := 0
for len(txqueue) > 0 {
if n, err := s.xconn.WriteBatch(txqueue, 0); err == nil {
for k := range txqueue[:n] {
nbytes += len(txqueue[k].Buffers[0])
}
npkts += n
txqueue = txqueue[n:]
} else {
// compatibility issue:
// for linux kernel<=2.6.32, support for sendmmsg is not available
// an error of type os.SyscallError will be returned
if operr, ok := err.(*net.OpError); ok {
if se, ok := operr.Err.(*os.SyscallError); ok {
if se.Syscall == "sendmmsg" {
s.xconnWriteError = se
s.defaultTx(txqueue)
return
}
}
}
s.notifyWriteError(errors.WithStack(err))
break
}
}
atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(npkts))
atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
}

97
gate-hk4e/mq/mq.go Normal file
View File

@@ -0,0 +1,97 @@
package mq
import (
"flswld.com/common/config"
"flswld.com/gate-hk4e-api/proto"
"flswld.com/logger"
"github.com/nats-io/nats.go"
"github.com/vmihailenco/msgpack/v5"
pb "google.golang.org/protobuf/proto"
)
type MessageQueue struct {
natsConn *nats.Conn
natsMsgChan chan *nats.Msg
netMsgInput chan *proto.NetMsg
netMsgOutput chan *proto.NetMsg
apiProtoMap *proto.ApiProtoMap
}
func NewMessageQueue(netMsgInput chan *proto.NetMsg, netMsgOutput chan *proto.NetMsg) (r *MessageQueue) {
r = new(MessageQueue)
conn, err := nats.Connect(config.CONF.MQ.NatsUrl)
if err != nil {
logger.LOG.Error("connect nats error: %v", err)
return nil
}
r.natsConn = conn
r.natsMsgChan = make(chan *nats.Msg, 10000)
_, err = r.natsConn.ChanSubscribe("GATE_HK4E", r.natsMsgChan)
if err != nil {
logger.LOG.Error("nats subscribe error: %v", err)
return nil
}
r.netMsgInput = netMsgInput
r.netMsgOutput = netMsgOutput
r.apiProtoMap = proto.NewApiProtoMap()
return r
}
func (m *MessageQueue) Start() {
go m.startRecvHandler()
go m.startSendHandler()
}
func (m *MessageQueue) Close() {
m.natsConn.Close()
}
func (m *MessageQueue) startRecvHandler() {
for {
natsMsg := <-m.natsMsgChan
// msgpack NetMsg
netMsg := new(proto.NetMsg)
err := msgpack.Unmarshal(natsMsg.Data, netMsg)
if err != nil {
logger.LOG.Error("parse bin to net msg error: %v", err)
continue
}
if netMsg.EventId == proto.NormalMsg {
// protobuf PayloadMessage
payloadMessage := m.apiProtoMap.GetProtoObjByApiId(netMsg.ApiId)
err = pb.Unmarshal(netMsg.PayloadMessageData, payloadMessage)
if err != nil {
logger.LOG.Error("parse bin to payload msg error: %v", err)
continue
}
netMsg.PayloadMessage = payloadMessage
}
m.netMsgOutput <- netMsg
}
}
func (m *MessageQueue) startSendHandler() {
for {
netMsg := <-m.netMsgInput
// protobuf PayloadMessage
payloadMessageData, err := pb.Marshal(netMsg.PayloadMessage)
if err != nil {
logger.LOG.Error("parse payload msg to bin error: %v", err)
continue
}
netMsg.PayloadMessageData = payloadMessageData
// msgpack NetMsg
netMsgData, err := msgpack.Marshal(netMsg)
if err != nil {
logger.LOG.Error("parse net msg to bin error: %v", err)
continue
}
natsMsg := nats.NewMsg("GAME_HK4E")
natsMsg.Data = netMsgData
err = m.natsConn.PublishMsg(natsMsg)
if err != nil {
logger.LOG.Error("nats publish msg error: %v", err)
continue
}
}
}

View File

@@ -0,0 +1,387 @@
package net
import (
"bytes"
"encoding/binary"
"flswld.com/common/config"
"flswld.com/common/utils/random"
"flswld.com/logger"
"gate-hk4e/kcp"
"io/ioutil"
"strconv"
"sync"
"time"
)
type KcpXorKey struct {
encKey []byte
decKey []byte
}
type KcpConnectManager struct {
openState bool
connMap map[uint64]*kcp.UDPSession
connMapLock sync.RWMutex
protoMsgInput chan *ProtoMsg
protoMsgOutput chan *ProtoMsg
kcpEventInput chan *KcpEvent
kcpEventOutput chan *KcpEvent
// 发送协程分发
kcpRawSendChanMap map[uint64]chan *ProtoMsg
kcpRawSendChanMapLock sync.RWMutex
// 收包发包监听标志
kcpRecvListenMap map[uint64]bool
kcpRecvListenMapLock sync.RWMutex
kcpSendListenMap map[uint64]bool
kcpSendListenMapLock sync.RWMutex
// key
dispatchKey []byte
secretKey []byte
kcpKeyMap map[uint64]*KcpXorKey
kcpKeyMapLock sync.RWMutex
// conv短时间内唯一生成
convGenMap map[uint64]int64
convGenMapLock sync.RWMutex
}
func NewKcpConnectManager(protoMsgInput chan *ProtoMsg, protoMsgOutput chan *ProtoMsg,
kcpEventInput chan *KcpEvent, kcpEventOutput chan *KcpEvent) (r *KcpConnectManager) {
r = new(KcpConnectManager)
r.openState = true
r.connMap = make(map[uint64]*kcp.UDPSession)
r.protoMsgInput = protoMsgInput
r.protoMsgOutput = protoMsgOutput
r.kcpEventInput = kcpEventInput
r.kcpEventOutput = kcpEventOutput
r.kcpRawSendChanMap = make(map[uint64]chan *ProtoMsg)
r.kcpRecvListenMap = make(map[uint64]bool)
r.kcpSendListenMap = make(map[uint64]bool)
r.kcpKeyMap = make(map[uint64]*KcpXorKey)
r.convGenMap = make(map[uint64]int64)
return r
}
func (k *KcpConnectManager) Start() {
go func() {
// key
var err error = nil
k.dispatchKey, err = ioutil.ReadFile("static/dispatchKey.bin")
if err != nil {
logger.LOG.Error("open dispatchKey.bin error")
return
}
k.secretKey, err = ioutil.ReadFile("static/secretKey.bin")
if err != nil {
logger.LOG.Error("open secretKey.bin error")
return
}
// kcp
port := strconv.FormatInt(int64(config.CONF.Hk4e.KcpPort), 10)
listener, err := kcp.ListenWithOptions("0.0.0.0:"+port, nil, 0, 0)
if err != nil {
logger.LOG.Error("listen kcp err: %v", err)
return
} else {
go k.enetHandle(listener)
go k.chanSendHandle()
go k.eventHandle()
for {
conn, err := listener.AcceptKCP()
if err != nil {
logger.LOG.Error("accept kcp err: %v", err)
return
}
if k.openState == false {
_ = conn.Close()
continue
}
conn.SetACKNoDelay(true)
conn.SetWriteDelay(false)
convId := conn.GetConv()
logger.LOG.Debug("client connect, convId: %v", convId)
// 连接建立成功通知
k.kcpEventOutput <- &KcpEvent{
ConvId: convId,
EventId: KcpConnEstNotify,
EventMessage: conn.RemoteAddr().String(),
}
k.connMapLock.Lock()
k.connMap[convId] = conn
k.connMapLock.Unlock()
k.kcpKeyMapLock.Lock()
k.kcpKeyMap[convId] = &KcpXorKey{
encKey: k.dispatchKey,
decKey: k.dispatchKey,
}
k.kcpKeyMapLock.Unlock()
go k.recvHandle(convId)
kcpRawSendChan := make(chan *ProtoMsg, 10000)
k.kcpRawSendChanMapLock.Lock()
k.kcpRawSendChanMap[convId] = kcpRawSendChan
k.kcpRawSendChanMapLock.Unlock()
go k.sendHandle(convId, kcpRawSendChan)
go k.rttMonitor(convId)
}
}
}()
go k.clearDeadConv()
}
func (k *KcpConnectManager) clearDeadConv() {
ticker := time.NewTicker(time.Minute)
for {
k.convGenMapLock.Lock()
now := time.Now().UnixNano()
oldConvList := make([]uint64, 0)
for conv, timestamp := range k.convGenMap {
if now-timestamp > int64(time.Hour) {
oldConvList = append(oldConvList, conv)
}
}
delConvList := make([]uint64, 0)
k.connMapLock.RLock()
for _, conv := range oldConvList {
_, exist := k.connMap[conv]
if !exist {
delConvList = append(delConvList, conv)
delete(k.convGenMap, conv)
}
}
k.connMapLock.RUnlock()
k.convGenMapLock.Unlock()
logger.LOG.Info("clean dead conv list: %v", delConvList)
<-ticker.C
}
}
func (k *KcpConnectManager) enetHandle(listener *kcp.Listener) {
for {
enetNotify := <-listener.EnetNotify
logger.LOG.Info("[Enet Notify], addr: %v, conv: %v, conn: %v, enet: %v", enetNotify.Addr, enetNotify.ConvId, enetNotify.ConnType, enetNotify.EnetType)
switch enetNotify.ConnType {
case kcp.ConnEnetSyn:
if enetNotify.EnetType == kcp.EnetClientConnectKey {
var conv uint64
k.convGenMapLock.Lock()
for {
convData := random.GetRandomByte(8)
convDataBuffer := bytes.NewBuffer(convData)
_ = binary.Read(convDataBuffer, binary.LittleEndian, &conv)
_, exist := k.convGenMap[conv]
if exist {
continue
} else {
k.convGenMap[conv] = time.Now().UnixNano()
break
}
}
k.convGenMapLock.Unlock()
listener.SendEnetNotifyToClient(&kcp.Enet{
Addr: enetNotify.Addr,
ConvId: conv,
ConnType: kcp.ConnEnetEst,
EnetType: enetNotify.EnetType,
})
}
case kcp.ConnEnetEst:
case kcp.ConnEnetFin:
k.closeKcpConn(enetNotify.ConvId, enetNotify.EnetType)
case kcp.ConnEnetAddrChange:
// 连接地址改变通知
k.kcpEventOutput <- &KcpEvent{
ConvId: enetNotify.ConvId,
EventId: KcpConnAddrChangeNotify,
EventMessage: enetNotify.Addr,
}
default:
}
}
}
func (k *KcpConnectManager) chanSendHandle() {
// 分发到每个连接具体的发送协程
for {
protoMsg := <-k.protoMsgInput
k.kcpRawSendChanMapLock.RLock()
kcpRawSendChan := k.kcpRawSendChanMap[protoMsg.ConvId]
k.kcpRawSendChanMapLock.RUnlock()
if kcpRawSendChan != nil {
select {
case kcpRawSendChan <- protoMsg:
default:
logger.LOG.Error("kcpRawSendChan is full, convId: %v", protoMsg.ConvId)
}
} else {
logger.LOG.Error("kcpRawSendChan is nil, convId: %v", protoMsg.ConvId)
}
}
}
func (k *KcpConnectManager) recvHandle(convId uint64) {
// 接收
k.connMapLock.RLock()
conn := k.connMap[convId]
k.connMapLock.RUnlock()
pktFreqLimitCounter := 0
pktFreqLimitTimer := time.Now().UnixNano()
protoEnDecode := NewProtoEnDecode()
recvBuf := make([]byte, conn.GetMaxPayloadLen())
for {
_ = conn.SetReadDeadline(time.Now().Add(time.Second * 30))
recvLen, err := conn.Read(recvBuf)
if err != nil {
logger.LOG.Error("exit recv loop, conn read err: %v, convId: %v", err, convId)
k.closeKcpConn(convId, kcp.EnetServerKick)
break
}
pktFreqLimitCounter++
now := time.Now().UnixNano()
if now-pktFreqLimitTimer > int64(time.Second) {
if pktFreqLimitCounter > 1000 {
logger.LOG.Error("exit recv loop, client packet send freq too high, convId: %v, pps: %v", convId, pktFreqLimitCounter)
k.closeKcpConn(convId, kcp.EnetPacketFreqTooHigh)
break
} else {
pktFreqLimitCounter = 0
}
pktFreqLimitTimer = now
}
recvData := recvBuf[:recvLen]
k.kcpRecvListenMapLock.RLock()
flag := k.kcpRecvListenMap[convId]
k.kcpRecvListenMapLock.RUnlock()
if flag {
// 收包通知
//recvMsg := make([]byte, len(recvData))
//copy(recvMsg, recvData)
k.kcpEventOutput <- &KcpEvent{
ConvId: convId,
EventId: KcpPacketRecvNotify,
EventMessage: recvData,
}
}
kcpMsgList := make([]*KcpMsg, 0)
k.decodeBinToPayload(recvData, convId, &kcpMsgList)
for _, v := range kcpMsgList {
protoMsgList := protoEnDecode.protoDecode(v)
for _, vv := range protoMsgList {
k.protoMsgOutput <- vv
}
}
}
}
func (k *KcpConnectManager) sendHandle(convId uint64, kcpRawSendChan chan *ProtoMsg) {
// 发送
k.connMapLock.RLock()
conn := k.connMap[convId]
k.connMapLock.RUnlock()
protoEnDecode := NewProtoEnDecode()
for {
protoMsg, ok := <-kcpRawSendChan
if !ok {
logger.LOG.Error("exit send loop, send chan close, convId: %v", convId)
k.closeKcpConn(convId, kcp.EnetServerKick)
break
}
kcpMsg := protoEnDecode.protoEncode(protoMsg)
if kcpMsg == nil {
logger.LOG.Error("decode kcp msg is nil, convId: %v", convId)
continue
}
bin := k.encodePayloadToBin(kcpMsg)
_ = conn.SetWriteDeadline(time.Now().Add(time.Second * 10))
_, err := conn.Write(bin)
if err != nil {
logger.LOG.Error("exit send loop, conn write err: %v, convId: %v", err, convId)
k.closeKcpConn(convId, kcp.EnetServerKick)
break
}
k.kcpSendListenMapLock.RLock()
flag := k.kcpSendListenMap[convId]
k.kcpSendListenMapLock.RUnlock()
if flag {
// 发包通知
k.kcpEventOutput <- &KcpEvent{
ConvId: convId,
EventId: KcpPacketSendNotify,
EventMessage: bin,
}
}
}
}
func (k *KcpConnectManager) rttMonitor(convId uint64) {
ticker := time.NewTicker(time.Second * 10)
for {
select {
case <-ticker.C:
k.connMapLock.RLock()
conn := k.connMap[convId]
k.connMapLock.RUnlock()
if conn == nil {
break
}
logger.LOG.Debug("convId: %v, RTO: %v, SRTT: %v, RTTVar: %v", convId, conn.GetRTO(), conn.GetSRTT(), conn.GetSRTTVar())
k.kcpEventOutput <- &KcpEvent{
ConvId: convId,
EventId: KcpConnRttNotify,
EventMessage: conn.GetSRTT(),
}
}
}
}
func (k *KcpConnectManager) closeKcpConn(convId uint64, enetType uint32) {
k.connMapLock.RLock()
conn, exist := k.connMap[convId]
k.connMapLock.RUnlock()
if !exist {
return
}
// 获取待关闭的发送管道
k.kcpRawSendChanMapLock.RLock()
kcpRawSendChan := k.kcpRawSendChanMap[convId]
k.kcpRawSendChanMapLock.RUnlock()
// 清理数据
k.connMapLock.Lock()
delete(k.connMap, convId)
k.connMapLock.Unlock()
k.kcpRawSendChanMapLock.Lock()
delete(k.kcpRawSendChanMap, convId)
k.kcpRawSendChanMapLock.Unlock()
k.kcpRecvListenMapLock.Lock()
delete(k.kcpRecvListenMap, convId)
k.kcpRecvListenMapLock.Unlock()
k.kcpSendListenMapLock.Lock()
delete(k.kcpSendListenMap, convId)
k.kcpSendListenMapLock.Unlock()
k.kcpKeyMapLock.Lock()
delete(k.kcpKeyMap, convId)
k.kcpKeyMapLock.Unlock()
// 关闭连接
conn.SendEnetNotify(&kcp.Enet{
ConnType: kcp.ConnEnetFin,
EnetType: enetType,
})
_ = conn.Close()
// 关闭发送管道
close(kcpRawSendChan)
// 连接关闭通知
k.kcpEventOutput <- &KcpEvent{
ConvId: convId,
EventId: KcpConnCloseNotify,
}
}
func (k *KcpConnectManager) closeAllKcpConn() {
closeConnList := make([]*kcp.UDPSession, 0)
k.connMapLock.RLock()
for _, v := range k.connMap {
closeConnList = append(closeConnList, v)
}
k.connMapLock.RUnlock()
for _, v := range closeConnList {
k.closeKcpConn(v.GetConv(), kcp.EnetServerShutdown)
}
}

View File

@@ -0,0 +1,187 @@
package net
import (
"bytes"
"encoding/binary"
"flswld.com/common/utils/endec"
"flswld.com/logger"
)
/*
原神KCP协议(带*为xor加密数据)
0 1 2 4 8(字节)
+---------------------------------------------------------------------------------------+
| conv |
+---------------------------------------------------------------------------------------+
| cmd | frg | wnd | ts |
+---------------------------------------------------------------------------------------+
| sn | una |
+---------------------------------------------------------------------------------------+
| len | 0X4567* | apiId* |
+---------------------------------------------------------------------------------------+
| headLen* | payloadLen* | head* |
+---------------------------------------------------------------------------------------+
| payload* | 0X89AB* |
+---------------------------------------------------------------------------------------+
*/
type KcpMsg struct {
ConvId uint64
ApiId uint16
HeadData []byte
ProtoData []byte
}
func (k *KcpConnectManager) decodeBinToPayload(data []byte, convId uint64, kcpMsgList *[]*KcpMsg) {
// xor解密
k.kcpKeyMapLock.RLock()
xorKey, exist := k.kcpKeyMap[convId]
k.kcpKeyMapLock.RUnlock()
if !exist {
logger.LOG.Error("kcp xor key not exist, convId: %v", convId)
return
}
endec.Xor(data, xorKey.decKey)
k.decodeRecur(data, convId, kcpMsgList)
}
func (k *KcpConnectManager) decodeRecur(data []byte, convId uint64, kcpMsgList *[]*KcpMsg) {
// 长度太短
if len(data) < 12 {
logger.LOG.Debug("packet len less 12 byte")
return
}
// 头部标志错误
if data[0] != 0x45 || data[1] != 0x67 {
logger.LOG.Error("packet head magic 0x4567 error")
return
}
// 协议号
apiIdByteSlice := make([]byte, 8)
apiIdByteSlice[6] = data[2]
apiIdByteSlice[7] = data[3]
apiIdBuffer := bytes.NewBuffer(apiIdByteSlice)
var apiId int64
err := binary.Read(apiIdBuffer, binary.BigEndian, &apiId)
if err != nil {
logger.LOG.Error("packet api id parse fail: %v", err)
return
}
// 头部长度
headLenByteSlice := make([]byte, 8)
headLenByteSlice[6] = data[4]
headLenByteSlice[7] = data[5]
headLenBuffer := bytes.NewBuffer(headLenByteSlice)
var headLen int64
err = binary.Read(headLenBuffer, binary.BigEndian, &headLen)
if err != nil {
logger.LOG.Error("packet head len parse fail: %v", err)
return
}
// proto长度
protoLenByteSlice := make([]byte, 8)
protoLenByteSlice[4] = data[6]
protoLenByteSlice[5] = data[7]
protoLenByteSlice[6] = data[8]
protoLenByteSlice[7] = data[9]
protoLenBuffer := bytes.NewBuffer(protoLenByteSlice)
var protoLen int64
err = binary.Read(protoLenBuffer, binary.BigEndian, &protoLen)
if err != nil {
logger.LOG.Error("packet proto len parse fail: %v", err)
return
}
// 检查最小长度
if len(data) < int(headLen+protoLen)+12 {
logger.LOG.Error("packet len error")
return
}
// 尾部标志错误
if data[headLen+protoLen+10] != 0x89 || data[headLen+protoLen+11] != 0xAB {
logger.LOG.Error("packet tail magic 0x89AB error")
return
}
// 判断是否有不止一个包
haveMoreData := false
if len(data) > int(headLen+protoLen)+12 {
haveMoreData = true
}
// 头部数据
headData := data[10 : 10+headLen]
// proto数据
protoData := data[10+headLen : 10+headLen+protoLen]
// 返回数据
kcpMsg := new(KcpMsg)
kcpMsg.ConvId = convId
kcpMsg.ApiId = uint16(apiId)
//kcpMsg.HeadData = make([]byte, len(headData))
//copy(kcpMsg.HeadData, headData)
//kcpMsg.ProtoData = make([]byte, len(protoData))
//copy(kcpMsg.ProtoData, protoData)
kcpMsg.HeadData = headData
kcpMsg.ProtoData = protoData
*kcpMsgList = append(*kcpMsgList, kcpMsg)
// 递归解析
if haveMoreData {
k.decodeRecur(data[int(headLen+protoLen)+12:], convId, kcpMsgList)
}
}
func (k *KcpConnectManager) encodePayloadToBin(kcpMsg *KcpMsg) (bin []byte) {
if kcpMsg.HeadData == nil {
kcpMsg.HeadData = make([]byte, 0)
}
if kcpMsg.ProtoData == nil {
kcpMsg.ProtoData = make([]byte, 0)
}
bin = make([]byte, len(kcpMsg.HeadData)+len(kcpMsg.ProtoData)+12)
// 头部标志
bin[0] = 0x45
bin[1] = 0x67
// 协议号
apiIdBuffer := bytes.NewBuffer([]byte{})
err := binary.Write(apiIdBuffer, binary.BigEndian, kcpMsg.ApiId)
if err != nil {
logger.LOG.Error("api id encode err: %v", err)
return nil
}
bin[2] = (apiIdBuffer.Bytes())[0]
bin[3] = (apiIdBuffer.Bytes())[1]
// 头部长度
headLenBuffer := bytes.NewBuffer([]byte{})
err = binary.Write(headLenBuffer, binary.BigEndian, uint16(len(kcpMsg.HeadData)))
if err != nil {
logger.LOG.Error("head len encode err: %v", err)
return nil
}
bin[4] = (headLenBuffer.Bytes())[0]
bin[5] = (headLenBuffer.Bytes())[1]
// proto长度
protoLenBuffer := bytes.NewBuffer([]byte{})
err = binary.Write(protoLenBuffer, binary.BigEndian, uint32(len(kcpMsg.ProtoData)))
if err != nil {
logger.LOG.Error("proto len encode err: %v", err)
return nil
}
bin[6] = (protoLenBuffer.Bytes())[0]
bin[7] = (protoLenBuffer.Bytes())[1]
bin[8] = (protoLenBuffer.Bytes())[2]
bin[9] = (protoLenBuffer.Bytes())[3]
// 头部数据
copy(bin[10:], kcpMsg.HeadData)
// proto数据
copy(bin[10+len(kcpMsg.HeadData):], kcpMsg.ProtoData)
// 尾部标志
bin[len(bin)-2] = 0x89
bin[len(bin)-1] = 0xAB
// xor加密
k.kcpKeyMapLock.RLock()
xorKey, exist := k.kcpKeyMap[kcpMsg.ConvId]
k.kcpKeyMapLock.RUnlock()
if !exist {
logger.LOG.Error("kcp xor key not exist, convId: %v", kcpMsg.ConvId)
return
}
endec.Xor(bin, xorKey.encKey)
return bin
}

134
gate-hk4e/net/kcp_event.go Normal file
View File

@@ -0,0 +1,134 @@
package net
import "flswld.com/logger"
const (
KcpXorKeyChange = iota
KcpPacketRecvListen
KcpPacketSendListen
KcpConnForceClose
KcpAllConnForceClose
KcpGateOpenState
KcpPacketRecvNotify
KcpPacketSendNotify
KcpConnCloseNotify
KcpConnEstNotify
KcpConnRttNotify
KcpConnAddrChangeNotify
)
type KcpEvent struct {
ConvId uint64
EventId int
EventMessage any
}
func (k *KcpConnectManager) eventHandle() {
// 事件处理
for {
event := <-k.kcpEventInput
logger.LOG.Info("kcp manager recv event, ConvId: %v, EventId: %v, EventMessage: %v", event.ConvId, event.EventId, event.EventMessage)
switch event.EventId {
case KcpXorKeyChange:
// XOR密钥切换
k.connMapLock.RLock()
_, exist := k.connMap[event.ConvId]
k.connMapLock.RUnlock()
if !exist {
logger.LOG.Error("conn not exist, convId: %v", event.ConvId)
continue
}
flag, ok := event.EventMessage.(string)
if !ok {
logger.LOG.Error("event KcpXorKeyChange msg type error")
continue
}
if flag == "ENC" {
k.kcpKeyMapLock.Lock()
k.kcpKeyMap[event.ConvId].encKey = k.secretKey
k.kcpKeyMapLock.Unlock()
} else if flag == "DEC" {
k.kcpKeyMapLock.Lock()
k.kcpKeyMap[event.ConvId].decKey = k.secretKey
k.kcpKeyMapLock.Unlock()
}
case KcpPacketRecvListen:
// 收包监听
k.connMapLock.RLock()
_, exist := k.connMap[event.ConvId]
k.connMapLock.RUnlock()
if !exist {
logger.LOG.Error("conn not exist, convId: %v", event.ConvId)
continue
}
flag, ok := event.EventMessage.(string)
if !ok {
logger.LOG.Error("event KcpXorKeyChange msg type error")
continue
}
if flag == "Enable" {
k.kcpRecvListenMapLock.Lock()
k.kcpRecvListenMap[event.ConvId] = true
k.kcpRecvListenMapLock.Unlock()
} else if flag == "Disable" {
k.kcpRecvListenMapLock.Lock()
k.kcpRecvListenMap[event.ConvId] = false
k.kcpRecvListenMapLock.Unlock()
}
case KcpPacketSendListen:
// 发包监听
k.connMapLock.RLock()
_, exist := k.connMap[event.ConvId]
k.connMapLock.RUnlock()
if !exist {
logger.LOG.Error("conn not exist, convId: %v", event.ConvId)
continue
}
flag, ok := event.EventMessage.(string)
if !ok {
logger.LOG.Error("event KcpXorKeyChange msg type error")
continue
}
if flag == "Enable" {
k.kcpSendListenMapLock.Lock()
k.kcpSendListenMap[event.ConvId] = true
k.kcpSendListenMapLock.Unlock()
} else if flag == "Disable" {
k.kcpSendListenMapLock.Lock()
k.kcpSendListenMap[event.ConvId] = false
k.kcpSendListenMapLock.Unlock()
}
case KcpConnForceClose:
// 强制关闭某个连接
k.connMapLock.RLock()
_, exist := k.connMap[event.ConvId]
k.connMapLock.RUnlock()
if !exist {
logger.LOG.Error("conn not exist, convId: %v", event.ConvId)
continue
}
reason, ok := event.EventMessage.(uint32)
if !ok {
logger.LOG.Error("event KcpConnForceClose msg type error")
continue
}
k.closeKcpConn(event.ConvId, reason)
logger.LOG.Info("conn has been force close, convId: %v", event.ConvId)
case KcpAllConnForceClose:
// 强制关闭所有连接
k.closeAllKcpConn()
logger.LOG.Info("all conn has been force close")
case KcpGateOpenState:
// 改变网关开放状态
openState, ok := event.EventMessage.(bool)
if !ok {
logger.LOG.Error("event KcpGateOpenState msg type error")
continue
}
k.openState = openState
if openState == false {
k.closeAllKcpConn()
}
}
}
}

View File

@@ -0,0 +1,160 @@
package net
import (
"flswld.com/gate-hk4e-api/proto"
"flswld.com/logger"
pb "google.golang.org/protobuf/proto"
)
type ProtoEnDecode struct {
apiProtoMap *proto.ApiProtoMap
}
func NewProtoEnDecode() (r *ProtoEnDecode) {
r = new(ProtoEnDecode)
r.apiProtoMap = proto.NewApiProtoMap()
return r
}
type ProtoMsg struct {
ConvId uint64
ApiId uint16
HeadMessage *proto.PacketHead
PayloadMessage pb.Message
}
type ProtoMessage struct {
apiId uint16
message pb.Message
}
func (p *ProtoEnDecode) protoDecode(kcpMsg *KcpMsg) (protoMsgList []*ProtoMsg) {
protoMsgList = make([]*ProtoMsg, 0)
protoMsg := new(ProtoMsg)
protoMsg.ConvId = kcpMsg.ConvId
protoMsg.ApiId = kcpMsg.ApiId
// head msg
if kcpMsg.HeadData != nil && len(kcpMsg.HeadData) != 0 {
headMsg := new(proto.PacketHead)
err := pb.Unmarshal(kcpMsg.HeadData, headMsg)
if err != nil {
logger.LOG.Error("unmarshal head data err: %v", err)
return protoMsgList
}
protoMsg.HeadMessage = headMsg
} else {
protoMsg.HeadMessage = nil
}
// payload msg
protoMessageList := make([]*ProtoMessage, 0)
p.protoDecodePayloadCore(kcpMsg.ApiId, kcpMsg.ProtoData, &protoMessageList)
if len(protoMessageList) == 0 {
logger.LOG.Error("decode proto object is nil")
return protoMsgList
}
if kcpMsg.ApiId == proto.ApiUnionCmdNotify {
for _, protoMessage := range protoMessageList {
msg := new(ProtoMsg)
msg.ConvId = kcpMsg.ConvId
msg.ApiId = protoMessage.apiId
msg.HeadMessage = protoMsg.HeadMessage
msg.PayloadMessage = protoMessage.message
//logger.LOG.Debug("[recv] union proto msg, convId: %v, apiId: %v", msg.ConvId, msg.ApiId)
if protoMessage.apiId == proto.ApiUnionCmdNotify {
// 聚合消息自身不再往后发送
continue
}
//logger.LOG.Debug("[recv] proto msg, convId: %v, apiId: %v, headMsg: %v", protoMsg.ConvId, protoMsg.ApiId, protoMsg.HeadMessage)
protoMsgList = append(protoMsgList, msg)
}
// 聚合消息自身不再往后发送
return protoMsgList
} else {
protoMsg.PayloadMessage = protoMessageList[0].message
}
//logger.LOG.Debug("[recv] proto msg, convId: %v, apiId: %v, headMsg: %v", protoMsg.ConvId, protoMsg.ApiId, protoMsg.HeadMessage)
protoMsgList = append(protoMsgList, protoMsg)
return protoMsgList
}
func (p *ProtoEnDecode) protoDecodePayloadCore(apiId uint16, protoData []byte, protoMessageList *[]*ProtoMessage) {
protoObj := p.decodePayloadToProto(apiId, protoData)
if protoObj == nil {
logger.LOG.Error("decode proto object is nil")
return
}
if apiId == proto.ApiUnionCmdNotify {
// 处理聚合消息
unionCmdNotify, ok := protoObj.(*proto.UnionCmdNotify)
if !ok {
logger.LOG.Error("parse union cmd error")
return
}
for _, cmd := range unionCmdNotify.GetCmdList() {
p.protoDecodePayloadCore(uint16(cmd.MessageId), cmd.Body, protoMessageList)
}
}
*protoMessageList = append(*protoMessageList, &ProtoMessage{
apiId: apiId,
message: protoObj,
})
}
func (p *ProtoEnDecode) protoEncode(protoMsg *ProtoMsg) (kcpMsg *KcpMsg) {
//logger.LOG.Debug("[send] proto msg, convId: %v, apiId: %v, headMsg: %v", protoMsg.ConvId, protoMsg.ApiId, protoMsg.HeadMessage)
kcpMsg = new(KcpMsg)
kcpMsg.ConvId = protoMsg.ConvId
kcpMsg.ApiId = protoMsg.ApiId
// head msg
if protoMsg.HeadMessage != nil {
headData, err := pb.Marshal(protoMsg.HeadMessage)
if err != nil {
logger.LOG.Error("marshal head data err: %v", err)
return nil
}
kcpMsg.HeadData = headData
} else {
kcpMsg.HeadData = nil
}
// payload msg
if protoMsg.PayloadMessage != nil {
apiId, protoData := p.encodeProtoToPayload(protoMsg.PayloadMessage)
if apiId == 0 || protoData == nil {
logger.LOG.Error("encode proto data is nil")
return nil
}
if apiId != 65535 && apiId != protoMsg.ApiId {
logger.LOG.Error("api id is not match with proto obj, src api id: %v, found api id: %v", protoMsg.ApiId, apiId)
return nil
}
kcpMsg.ProtoData = protoData
} else {
kcpMsg.ProtoData = nil
}
return kcpMsg
}
func (p *ProtoEnDecode) decodePayloadToProto(apiId uint16, protoData []byte) (protoObj pb.Message) {
protoObj = p.apiProtoMap.GetProtoObjByApiId(apiId)
if protoObj == nil {
logger.LOG.Error("get new proto object is nil")
return nil
}
err := pb.Unmarshal(protoData, protoObj)
if err != nil {
logger.LOG.Error("unmarshal proto data err: %v", err)
return nil
}
return protoObj
}
func (p *ProtoEnDecode) encodeProtoToPayload(protoObj pb.Message) (apiId uint16, protoData []byte) {
apiId = p.apiProtoMap.GetApiIdByProtoObj(protoObj)
var err error = nil
protoData, err = pb.Marshal(protoObj)
if err != nil {
logger.LOG.Error("marshal proto object err: %v", err)
return 0, nil
}
return apiId, protoData
}

175
gate-hk4e/region/region.go Normal file
View File

@@ -0,0 +1,175 @@
package region
import (
"encoding/base64"
"flswld.com/common/utils/endec"
"flswld.com/gate-hk4e-api/proto"
"flswld.com/logger"
pb "google.golang.org/protobuf/proto"
"io/ioutil"
)
func LoadRsaKey() (signRsaKey []byte, encRsaKeyMap map[string][]byte, pwdRsaKey []byte) {
var err error = nil
encRsaKeyMap = make(map[string][]byte)
signRsaKey, err = ioutil.ReadFile("static/region_sign_key.pem")
if err != nil {
logger.LOG.Error("open region_sign_key.pem error: %v", err)
return nil, nil, nil
}
encKeyIdList := []string{"2", "3", "4", "5"}
for _, v := range encKeyIdList {
encRsaKeyMap[v], err = ioutil.ReadFile("static/region_enc_key_" + v + ".pem")
if err != nil {
logger.LOG.Error("open region_enc_key_3.pem error: %v", err)
return nil, nil, nil
}
}
pwdRsaKey, err = ioutil.ReadFile("static/account_password_key.pem")
if err != nil {
logger.LOG.Error("open account_password_key.pem error: %v", err)
return nil, nil, nil
}
return signRsaKey, encRsaKeyMap, pwdRsaKey
}
func InitRegion(kcpAddr string, kcpPort int) (*proto.QueryCurrRegionHttpRsp, *proto.QueryRegionListHttpRsp) {
dispatchKey, err := ioutil.ReadFile("static/dispatchKey.bin")
if err != nil {
logger.LOG.Error("open dispatchKey.bin error: %v", err)
return nil, nil
}
dispatchSeed, err := ioutil.ReadFile("static/dispatchSeed.bin")
if err != nil {
logger.LOG.Error("open dispatchSeed.bin error: %v", err)
return nil, nil
}
// RegionCurr
regionCurr := new(proto.QueryCurrRegionHttpRsp)
regionCurr.RegionInfo = &proto.RegionInfo{
GateserverIp: kcpAddr,
GateserverPort: uint32(kcpPort),
SecretKey: dispatchSeed,
}
// RegionList
customConfigStr := `
{
"sdkenv": "2",
"checkdevice": "false",
"loadPatch": "false",
"showexception": "false",
"regionConfig": "pm|fk|add",
"downloadMode": "0",
}
`
customConfig := []byte(customConfigStr)
endec.Xor(customConfig, dispatchKey)
serverList := make([]*proto.RegionSimpleInfo, 0)
server := &proto.RegionSimpleInfo{
Name: "os_usa",
Title: "America",
Type: "DEV_PUBLIC",
DispatchUrl: "https://osusadispatch.yuanshen.com/query_cur_region",
}
serverList = append(serverList, server)
regionList := new(proto.QueryRegionListHttpRsp)
regionList.RegionList = serverList
regionList.ClientSecretKey = dispatchSeed
regionList.ClientCustomConfigEncrypted = customConfig
regionList.EnableLoginPc = true
return regionCurr, regionList
}
// 新的region构建方式已经不需要读取query_region_list和query_cur_region文件了 并跳过了版本校验的blk补丁下载
func _InitRegion(log *logger.Logger, kcpAddr string, kcpPort int) (*proto.QueryCurrRegionHttpRsp, *proto.QueryRegionListHttpRsp) {
// TODO 总有一天要把这些烦人的数据全部自己构造别再他妈的读文件了
// 加载文件
regionListKeyFile, err := ioutil.ReadFile("static/query_region_list_key")
if err != nil {
log.Error("open query_region_list_key error")
return nil, nil
}
regionCurrKeyFile, err := ioutil.ReadFile("static/query_cur_region_key")
if err != nil {
log.Error("open query_cur_region_key error")
return nil, nil
}
regionListFile, err := ioutil.ReadFile("static/query_region_list")
if err != nil {
log.Error("open query_region_list error")
return nil, nil
}
regionCurrFile, err := ioutil.ReadFile("static/query_cur_region")
if err != nil {
log.Error("open query_cur_region error")
return nil, nil
}
regionListKeyBin, err := base64.StdEncoding.DecodeString(string(regionListKeyFile))
if err != nil {
log.Error("decode query_region_list_key error")
return nil, nil
}
regionCurrKeyBin, err := base64.StdEncoding.DecodeString(string(regionCurrKeyFile))
if err != nil {
log.Error("decode query_cur_region_key error")
return nil, nil
}
regionListBin, err := base64.StdEncoding.DecodeString(string(regionListFile))
if err != nil {
log.Error("decode query_region_list error")
return nil, nil
}
regionCurrBin, err := base64.StdEncoding.DecodeString(string(regionCurrFile))
if err != nil {
log.Error("decode query_cur_region error")
return nil, nil
}
regionListKey := new(proto.QueryRegionListHttpRsp)
err = pb.Unmarshal(regionListKeyBin, regionListKey)
if err != nil {
log.Error("Unmarshal QueryRegionListHttpRsp error")
return nil, nil
}
regionCurrKey := new(proto.QueryCurrRegionHttpRsp)
err = pb.Unmarshal(regionCurrKeyBin, regionCurrKey)
if err != nil {
log.Error("Unmarshal QueryCurrRegionHttpRsp error")
return nil, nil
}
regionList := new(proto.QueryRegionListHttpRsp)
err = pb.Unmarshal(regionListBin, regionList)
if err != nil {
log.Error("Unmarshal QueryRegionListHttpRsp error")
return nil, nil
}
regionCurr := new(proto.QueryCurrRegionHttpRsp)
err = pb.Unmarshal(regionCurrBin, regionCurr)
if err != nil {
log.Error("Unmarshal QueryCurrRegionHttpRsp error")
return nil, nil
}
secretKey, err := ioutil.ReadFile("static/dispatchSeed.bin")
if err != nil {
log.Error("open dispatchSeed.bin error")
return nil, nil
}
// RegionCurr
regionInfo := regionCurr.GetRegionInfo()
regionInfo.GateserverIp = kcpAddr
regionInfo.GateserverPort = uint32(kcpPort)
regionInfo.SecretKey = secretKey
regionCurr.RegionInfo = regionInfo
// RegionList
serverList := make([]*proto.RegionSimpleInfo, 0)
server := &proto.RegionSimpleInfo{
Name: "os_usa",
Title: "America",
Type: "DEV_PUBLIC",
DispatchUrl: "https://osusadispatch.yuanshen.com/query_cur_region",
}
serverList = append(serverList, server)
regionList.RegionList = serverList
regionList.ClientSecretKey = regionListKey.ClientSecretKey
regionList.ClientCustomConfigEncrypted = regionListKey.ClientCustomConfigEncrypted
return regionCurr, regionList
}

File diff suppressed because one or more lines are too long

13
gate-hk4e/rpc/rpc.go Normal file
View File

@@ -0,0 +1,13 @@
package rpc
import "gate-hk4e/forward"
type RpcManager struct {
forwardManager *forward.ForwardManager
}
func NewRpcManager(forwardManager *forward.ForwardManager) (r *RpcManager) {
r = new(RpcManager)
r.forwardManager = forwardManager
return r
}

View File

@@ -0,0 +1,62 @@
package rpc
import (
"flswld.com/gate-hk4e-api/gm"
"github.com/pkg/errors"
)
// rpc interface
// 改变网关开放状态
func (r *RpcManager) ChangeGateOpenState(isOpen *bool, result *bool) error {
if isOpen == nil || result == nil {
return errors.New("param is nil")
}
*result = r.forwardManager.ChangeGateOpenState(*isOpen)
return nil
}
// 剔除玩家下线
func (r *RpcManager) KickPlayer(info *gm.KickPlayerInfo, result *bool) error {
if info == nil || result == nil {
return errors.New("param is nil")
}
*result = r.forwardManager.KickPlayer(info)
return nil
}
// 获取网关在线玩家信息
func (r *RpcManager) GetOnlineUser(uid *uint32, list *gm.OnlineUserList) error {
if uid == nil || list == nil {
return errors.New("param is nil")
}
list = r.forwardManager.GetOnlineUser(*uid)
return nil
}
// 用户密码改变
func (r *RpcManager) UserPasswordChange(uid *uint32, result *bool) error {
if uid == nil || result == nil {
return errors.New("param is nil")
}
*result = r.forwardManager.UserPasswordChange(*uid)
return nil
}
// 封号
func (r *RpcManager) ForbidUser(info *gm.ForbidUserInfo, result *bool) error {
if info == nil || result == nil {
return errors.New("param is nil")
}
*result = r.forwardManager.ForbidUser(info)
return nil
}
// 解封
func (r *RpcManager) UnForbidUser(uid *uint32, result *bool) error {
if uid == nil || result == nil {
return errors.New("param is nil")
}
*result = r.forwardManager.UnForbidUser(*uid)
return nil
}