feat: gateway
This commit is contained in:
86
services/gateway/auth.go
Normal file
86
services/gateway/auth.go
Normal file
@@ -0,0 +1,86 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"tg-mc/conf"
|
||||
"tg-mc/defs"
|
||||
"tg-mc/models"
|
||||
|
||||
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||
)
|
||||
|
||||
type Auth interface {
|
||||
IsAuthed(u models.User) bool
|
||||
RequestAuth(u models.User, req *LoginRequest)
|
||||
Reject(u models.User)
|
||||
SetAuth(u models.User)
|
||||
}
|
||||
|
||||
type Authcator struct {
|
||||
UserMap *sync.Map
|
||||
}
|
||||
|
||||
var authcator *Authcator
|
||||
|
||||
func GetAuthcator() Auth {
|
||||
if authcator == nil {
|
||||
authcator = &Authcator{
|
||||
UserMap: &sync.Map{},
|
||||
}
|
||||
}
|
||||
return authcator
|
||||
}
|
||||
|
||||
func (a *Authcator) getUserLoginReq(u models.User) *LoginRequest {
|
||||
chAny, ok := a.UserMap.Load(u.MCName)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
loginReq, ok := chAny.(*LoginRequest)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return loginReq
|
||||
}
|
||||
|
||||
func (a *Authcator) IsAuthed(u models.User) bool {
|
||||
loginReq := a.getUserLoginReq(u)
|
||||
if loginReq == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return <-loginReq.Resolve
|
||||
}
|
||||
|
||||
func (a *Authcator) RequestAuth(u models.User, req *LoginRequest) {
|
||||
a.UserMap.Store(u.MCName, req)
|
||||
m := tgbotapi.NewMessage(u.TGID, fmt.Sprintf("MC用户:%v 尝试登录,请选择操作", u.MCName))
|
||||
|
||||
m.ReplyMarkup = tgbotapi.NewInlineKeyboardMarkup(
|
||||
tgbotapi.NewInlineKeyboardRow(
|
||||
tgbotapi.NewInlineKeyboardButtonData("批准", defs.NewApproveCommand(u.MCName).ToJSON()),
|
||||
tgbotapi.NewInlineKeyboardButtonData("拒绝", defs.NewRejectCommand(u.MCName).ToJSON())),
|
||||
)
|
||||
conf.Bot.Send(m)
|
||||
}
|
||||
|
||||
func (a *Authcator) Reject(u models.User) {
|
||||
loginReq := a.getUserLoginReq(u)
|
||||
if loginReq == nil {
|
||||
return
|
||||
}
|
||||
|
||||
loginReq.Resolve <- false
|
||||
}
|
||||
|
||||
func (a *Authcator) SetAuth(u models.User) {
|
||||
loginReq := a.getUserLoginReq(u)
|
||||
if loginReq == nil {
|
||||
return
|
||||
}
|
||||
|
||||
loginReq.Resolve <- true
|
||||
}
|
214
services/gateway/gw.go
Normal file
214
services/gateway/gw.go
Normal file
@@ -0,0 +1,214 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"tg-mc/conf"
|
||||
"tg-mc/models"
|
||||
"time"
|
||||
|
||||
"github.com/Tnze/go-mc/net"
|
||||
pk "github.com/Tnze/go-mc/net/packet"
|
||||
"github.com/Tnze/go-mc/offline"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type LoginRequest struct {
|
||||
ChatID int64
|
||||
Resolve chan bool
|
||||
}
|
||||
|
||||
func requestVerify(mcid string) (bool, error) {
|
||||
|
||||
user, err := models.GetUserByMCName(mcid)
|
||||
if err != nil {
|
||||
return false, errors.New("mcid not found in settings")
|
||||
}
|
||||
|
||||
// Send a message to the user to accept or reject using your bot logic
|
||||
logrus.Infof("Requesting verification for MCID: %s with ChatID: %d\n", mcid, user.TGID)
|
||||
|
||||
// The channel to await a response from the verification process
|
||||
loginRequest := &LoginRequest{
|
||||
ChatID: user.TGID,
|
||||
Resolve: make(chan bool),
|
||||
}
|
||||
|
||||
GetAuthcator().RequestAuth(user, loginRequest)
|
||||
|
||||
select {
|
||||
case result := <-loginRequest.Resolve:
|
||||
return result, nil
|
||||
case <-time.After(10 * time.Second): // Replace with your actual timeout handling logic
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// This function should be called when a new client connection is accepted
|
||||
func HandleClientConnection(clientConn net.Conn) {
|
||||
settings := conf.GetBotSettings().GatewaySettings
|
||||
|
||||
targetConns, err := net.DialMC(settings.ServerHost + ":" + strconv.Itoa(settings.ServerPort))
|
||||
|
||||
targetConn := *targetConns
|
||||
|
||||
// targetConn, err := gonet.Dial("tcp", settings.ServerHost+":"+strconv.Itoa(settings.ServerPort))
|
||||
if err != nil {
|
||||
logrus.Errorf("Failed to connect to target: %s", err)
|
||||
return
|
||||
}
|
||||
protocol, intention, err := handshake(clientConn, targetConn)
|
||||
if err != nil {
|
||||
logrus.Errorf("Handshake error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
logrus.Infof("Protocol: %v, Intention: %v", protocol, intention)
|
||||
|
||||
switch intention {
|
||||
default: // unknown error
|
||||
logrus.Errorf("Unknown handshake intention: %v", intention)
|
||||
case 1: // for status
|
||||
handleProxyConnection(clientConn, targetConn)
|
||||
case 2: // for login
|
||||
handlePlaying(clientConn, targetConn)
|
||||
}
|
||||
}
|
||||
|
||||
type PlayerInfo struct {
|
||||
Name string
|
||||
UUID uuid.UUID
|
||||
OPLevel int
|
||||
}
|
||||
|
||||
func handlePlaying(conn, target net.Conn) {
|
||||
// login, get player info
|
||||
info, err := acceptLogin(conn, target)
|
||||
if err != nil {
|
||||
logrus.Errorf("user [%s] Login failed", info.Name)
|
||||
return
|
||||
}
|
||||
|
||||
logrus.Infof("Login successful: %s", info.Name)
|
||||
|
||||
// Write LoginSuccess packet
|
||||
|
||||
handleProxyConnection(conn, target)
|
||||
}
|
||||
|
||||
// acceptLogin check player's account
|
||||
func acceptLogin(conn, target net.Conn) (info PlayerInfo, err error) {
|
||||
// login start
|
||||
var p pk.Packet
|
||||
err = conn.ReadPacket(&p)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = p.Scan((*pk.String)(&info.Name)) // decode username as pk.String
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if ok, err := requestVerify(info.Name); !ok || err != nil {
|
||||
err = errors.New("verify failed")
|
||||
conn.Close()
|
||||
target.Close()
|
||||
return info, err
|
||||
}
|
||||
|
||||
if err := target.WritePacket(p); err != nil {
|
||||
return info, err
|
||||
}
|
||||
|
||||
// auth
|
||||
const OnlineMode = false
|
||||
if OnlineMode {
|
||||
info.UUID = offline.NameToUUID(info.Name)
|
||||
} else {
|
||||
// offline-mode UUID
|
||||
info.UUID = offline.NameToUUID(info.Name)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func handshake(conn, target net.Conn) (protocol, intention int32, err error) {
|
||||
var (
|
||||
p pk.Packet
|
||||
Protocol, Intention pk.VarInt
|
||||
ServerAddress pk.String // ignored
|
||||
ServerPort pk.UnsignedShort // ignored
|
||||
)
|
||||
// receive handshake packet
|
||||
if err = conn.ReadPacket(&p); err != nil {
|
||||
return
|
||||
}
|
||||
err = p.Scan(&Protocol, &ServerAddress, &ServerPort, &Intention)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = target.WritePacket(p)
|
||||
|
||||
return int32(Protocol), int32(Intention), err
|
||||
}
|
||||
|
||||
func handleProxyConnection(clientConn net.Conn, targetConn net.Conn) {
|
||||
defer clientConn.Close()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
var p pk.Packet
|
||||
if err := clientConn.ReadPacket(&p); err != nil {
|
||||
logrus.Errorf("ReadPacket error: %v", err)
|
||||
break
|
||||
}
|
||||
if err := targetConn.WritePacket(p); err != nil {
|
||||
logrus.Errorf("WritePacket error: %v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
func() {
|
||||
for {
|
||||
var p pk.Packet
|
||||
if err := targetConn.ReadPacket(&p); err != nil {
|
||||
logrus.Errorf("ReadPacket error: %v", err)
|
||||
break
|
||||
}
|
||||
if err := clientConn.WritePacket(p); err != nil {
|
||||
logrus.Errorf("WritePacket error: %v", err)
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func startProxyServer(proxyHost string, proxyPort int) {
|
||||
|
||||
settings := conf.GetBotSettings().GatewaySettings
|
||||
|
||||
l, err := net.ListenMC(fmt.Sprintf(":%d", settings.ProxyPort))
|
||||
if err != nil {
|
||||
logrus.Fatalf("Error starting proxy server: %s", err)
|
||||
}
|
||||
defer l.Close()
|
||||
logrus.Infof("Proxy server started on %s:%d", proxyHost, proxyPort)
|
||||
|
||||
for {
|
||||
clientConn, err := l.Accept()
|
||||
if err != nil {
|
||||
logrus.Errorf("Error accepting connection: %s", err)
|
||||
continue
|
||||
}
|
||||
go HandleClientConnection(clientConn)
|
||||
}
|
||||
}
|
||||
|
||||
func StartGateway() {
|
||||
settings := conf.GetBotSettings().GatewaySettings
|
||||
startProxyServer(settings.ProxyHost, settings.ProxyPort)
|
||||
}
|
Reference in New Issue
Block a user