multiserver/command.go

603 lines
14 KiB
Go
Raw Normal View History

2021-01-24 05:00:26 -08:00
package main
2021-01-14 04:40:31 -08:00
2021-01-16 12:48:21 -08:00
import (
2021-02-23 11:09:00 -08:00
"bytes"
2021-01-16 12:48:21 -08:00
"crypto/subtle"
"encoding/binary"
2021-03-29 09:57:30 -07:00
"io"
2021-01-16 12:48:21 -08:00
"log"
"github.com/HimbeerserverDE/srp"
"github.com/anon55555/mt/rudp"
2021-01-16 12:48:21 -08:00
)
2021-01-14 04:40:31 -08:00
const (
2021-01-14 08:09:06 -08:00
ToClientHello = 0x02
ToClientAuthAccept = 0x03
ToClientAcceptSudoMode = 0x04
ToClientDenySudoMode = 0x05
ToClientAccessDenied = 0x0A
ToClientBlockdata = 0x20
ToClientAddNode = 0x21
ToClientRemoveNode = 0x22
ToClientInventory = 0x27
ToClientTimeOfDay = 0x29
ToClientCsmRestrictionFlags = 0x2A
ToClientPlayerSpeed = 0x2B
ToClientMediaPush = 0x2C
ToClientChatMessage = 0x2F
2021-01-14 04:40:31 -08:00
ToClientActiveObjectRemoveAdd = 0x31
2021-01-14 08:09:06 -08:00
ToClientActiveObjectMessages = 0x32
ToClientHp = 0x33
ToClientMovePlayer = 0x34
ToClientFov = 0x36
ToClientDeathscreen = 0x37
ToClientMedia = 0x38
ToClientTooldef = 0x39
ToClientNodedef = 0x3A
ToClientCraftitemdef = 0x3B
ToClientAnnounceMedia = 0x3C
ToClientItemdef = 0x3D
ToClientPlaySound = 0x3F
ToClientStopSound = 0x40
ToClientPrivileges = 0x41
ToClientInventoryFormspec = 0x42
ToClientDetachedInventory = 0x43
ToClientShowFormspec = 0x44
ToClientMovement = 0x45
ToClientSpawnParticle = 0x46
ToClientAddParticlespawner = 0x47
ToClientHudAdd = 0x49
ToClientHudRm = 0x4A
ToClientHudChange = 0x4B
ToClientHudSetFlags = 0x4C
ToClientHudSetParam = 0x4D
ToClientBreath = 0x4E
ToClientSetSky = 0x4F
2021-01-14 04:40:31 -08:00
ToClientOverrideDayNightRatio = 0x50
ToClientLocalPlayerAnimations = 0x51
2021-01-14 08:09:06 -08:00
ToClientEyeOffset = 0x52
2021-01-14 04:40:31 -08:00
ToClientDeleteParticlespawner = 0x53
2021-01-14 08:09:06 -08:00
ToClientCloudParams = 0x54
ToClientFadeSound = 0x55
ToClientUpdatePlayerList = 0x56
ToClientModChannelMsg = 0x57
ToClientModChannelSignal = 0x58
2021-01-14 08:09:06 -08:00
ToClientNodeMetaChanged = 0x59
ToClientSetSun = 0x5A
ToClientSetMoon = 0x5B
ToClientSetStars = 0x5C
ToClientSrpBytesSB = 0x60
ToClientFormspecPrepend = 0x61
ToClientMinimapModes = 0x62
2021-01-14 04:40:31 -08:00
)
const (
2021-01-14 08:09:06 -08:00
ToServerInit = 0x02
ToServerInit2 = 0x11
ToServerModChannelJoin = 0x17
ToServerModChannelLeave = 0x18
ToServerModChannelMsg = 0x19
2021-01-14 08:09:06 -08:00
ToServerPlayerPos = 0x23
ToServerGotblocks = 0x24
ToServerDeletedblocks = 0x25
2021-01-14 04:40:31 -08:00
ToServerInventoryAction = 0x31
2021-01-14 08:09:06 -08:00
ToServerChatMessage = 0x32
ToServerDamage = 0x35
ToServerPlayerItem = 0x37
ToServerRespawn = 0x38
ToServerInteract = 0x39
ToServerRemovedSounds = 0x3A
ToServerNodeMetaFields = 0x3B
2021-01-14 04:40:31 -08:00
ToServerInventoryFields = 0x3C
2021-01-14 08:09:06 -08:00
ToServerRequestMedia = 0x40
ToServerClientReady = 0x43
ToServerFirstSrp = 0x50
ToServerSrpBytesA = 0x51
ToServerSrpBytesM = 0x52
2021-01-14 04:40:31 -08:00
)
const (
AccessDeniedWrongPassword = iota
AccessDeniedUnexpectedData
AccessDeniedSingleplayer
AccessDeniedWrongVersion
AccessDeniedWrongCharsInName
AccessDeniedWrongName
AccessDeniedTooManyUsers
AccessDeniedEmptyPassword
AccessDeniedAlreadyConnected
AccessDeniedServerFail
AccessDeniedCustomString
AccessDeniedShutdown
AccessDeniedCrash
)
2021-03-29 09:57:30 -07:00
func processPktCommand(src, dst *Conn, pkt *rudp.Pkt) bool {
r := ByteReader(*pkt)
origReader := *r
pkt.Reader = &origReader
cmdBytes := make([]byte, 2)
r.Read(cmdBytes)
2021-01-14 08:09:06 -08:00
if src.IsSrv() {
2021-03-29 09:57:30 -07:00
switch cmd := binary.BigEndian.Uint16(cmdBytes); cmd {
2021-01-14 08:09:06 -08:00
case ToClientActiveObjectRemoveAdd:
2021-03-29 09:57:30 -07:00
pkt.Reader = bytes.NewReader(append(cmdBytes, processAoRmAdd(dst, r)...))
2021-01-14 08:09:06 -08:00
return false
2021-02-20 04:17:52 -08:00
case ToClientActiveObjectMessages:
2021-03-29 09:57:30 -07:00
pkt.Reader = bytes.NewReader(append(cmdBytes, processAoMsgs(dst, r)...))
2021-02-20 04:17:52 -08:00
return false
2021-01-17 02:08:34 -08:00
case ToClientChatMessage:
2021-03-29 09:57:30 -07:00
r.Seek(2, io.SeekCurrent)
namelenBytes := make([]byte, 2)
r.Read(namelenBytes)
namelen := binary.BigEndian.Uint16(namelenBytes)
r.Seek(int64(namelen), io.SeekCurrent)
msglenBytes := make([]byte, 2)
r.Read(msglenBytes)
msglen := binary.BigEndian.Uint16(msglenBytes)
msg := make([]byte, r.Len())
r.Read(msg)
2021-01-17 02:08:34 -08:00
data := make([]byte, 4+msglen*2)
data[0] = uint8(0x00)
data[1] = uint8(ToServerChatMessage)
binary.BigEndian.PutUint16(data[2:4], uint16(msglen))
copy(data[4:], msg)
2021-03-29 09:57:30 -07:00
return processServerChatMessage(dst, rudp.Pkt{
Reader: bytes.NewReader(data),
PktInfo: rudp.PktInfo{
Channel: pkt.Channel,
},
})
case ToClientModChannelSignal:
2021-03-29 09:57:30 -07:00
r.Seek(1, io.SeekCurrent)
chlenBytes := make([]byte, 2)
r.Read(chlenBytes)
chlen := binary.BigEndian.Uint16(chlenBytes)
chBytes := make([]byte, chlen)
r.Read(chBytes)
state, _ := r.ReadByte()
r.Seek(2, io.SeekStart)
ch := string(chBytes)
if ch == rpcCh {
2021-03-29 09:57:30 -07:00
switch sig, _ := r.ReadByte(); sig {
case ModChSigJoinOk:
2021-02-13 08:54:30 -08:00
src.SetUseRpc(true)
case ModChSigSetState:
if state == ModChStateRO {
2021-02-13 08:54:30 -08:00
src.SetUseRpc(false)
}
}
return true
}
2021-03-29 09:57:30 -07:00
return false
case ToClientModChannelMsg:
2021-03-29 09:57:30 -07:00
return processRpc(src, r)
2021-02-09 23:35:14 -08:00
case ToClientBlockdata:
2021-03-29 09:57:30 -07:00
data, drop := processBlockdata(dst, r)
if drop {
return true
}
pkt.Reader = bytes.NewReader(append(cmdBytes, data...))
return false
2021-02-10 22:40:42 -08:00
case ToClientAddNode:
2021-03-29 09:57:30 -07:00
pkt.Reader = bytes.NewReader(append(cmdBytes, processAddnode(dst, r)...))
return false
2021-02-13 10:52:41 -08:00
case ToClientHudAdd:
2021-03-29 09:57:30 -07:00
idBytes := make([]byte, 4)
r.Read(idBytes)
id := binary.BigEndian.Uint32(idBytes)
2021-02-13 10:52:41 -08:00
dst.huds[id] = true
return false
case ToClientHudRm:
2021-03-29 09:57:30 -07:00
idBytes := make([]byte, 4)
r.Read(idBytes)
id := binary.BigEndian.Uint32(idBytes)
2021-02-13 10:52:41 -08:00
dst.huds[id] = false
return false
2021-02-21 03:04:54 -08:00
case ToClientPlaySound:
2021-03-29 09:57:30 -07:00
idBytes := make([]byte, 4)
r.Read(idBytes)
id := int32(binary.BigEndian.Uint32(idBytes))
namelenBytes := make([]byte, 2)
r.Read(namelenBytes)
namelen := binary.BigEndian.Uint16(namelenBytes)
r.Seek(int64(17+namelen), io.SeekStart)
objIDBytes := make([]byte, 2)
r.Read(objIDBytes)
objID := binary.BigEndian.Uint16(objIDBytes)
2021-02-21 03:04:54 -08:00
if objID == dst.currentPlayerCao {
objID = dst.localPlayerCao
} else if objID == dst.localPlayerCao {
objID = dst.currentPlayerCao
}
2021-03-29 09:57:30 -07:00
r.Seek(2, io.SeekStart)
data := make([]byte, r.Len())
r.Read(data)
binary.BigEndian.PutUint16(data[17+namelen:19+namelen], objID)
pkt.Reader = bytes.NewReader(append(cmdBytes, data...))
if loop, _ := r.ReadByte(); loop > 0 {
2021-02-21 03:04:54 -08:00
dst.sounds[id] = true
}
case ToClientStopSound:
2021-03-29 09:57:30 -07:00
idBytes := make([]byte, 4)
r.Read(idBytes)
id := int32(binary.BigEndian.Uint32(idBytes))
2021-02-21 03:04:54 -08:00
dst.sounds[id] = false
2021-02-21 03:24:07 -08:00
case ToClientAddParticlespawner:
2021-03-29 09:57:30 -07:00
r.Seek(97, io.SeekStart)
texturelenBytes := make([]byte, 4)
r.Read(texturelenBytes)
texturelen := binary.BigEndian.Uint32(texturelenBytes)
r.Seek(int64(6+texturelen), io.SeekCurrent)
idBytes := make([]byte, 2)
r.Read(idBytes)
id := binary.BigEndian.Uint16(idBytes)
2021-02-21 03:24:07 -08:00
if id == dst.currentPlayerCao {
id = dst.localPlayerCao
} else if id == dst.localPlayerCao {
id = dst.currentPlayerCao
}
2021-03-29 09:57:30 -07:00
r.Seek(2, io.SeekStart)
data := make([]byte, r.Len())
r.Read(data)
binary.BigEndian.PutUint16(data[107+texturelen:109+texturelen], id)
pkt.Reader = bytes.NewReader(append(cmdBytes, data...))
2021-02-23 11:09:00 -08:00
case ToClientInventory:
2021-03-29 09:57:30 -07:00
if err := dst.Inv().Deserialize(r); err != nil {
2021-02-23 11:09:00 -08:00
return true
}
2021-02-25 00:11:37 -08:00
dst.UpdateHandCapabs()
2021-02-23 11:09:00 -08:00
buf := &bytes.Buffer{}
dst.Inv().Serialize(buf)
2021-02-23 11:11:01 -08:00
2021-03-29 09:57:30 -07:00
pkt.Reader = bytes.NewReader(append(cmdBytes, buf.Bytes()...))
2021-02-23 11:09:00 -08:00
return false
2021-03-02 09:30:59 -08:00
case ToClientAccessDenied:
2021-03-09 13:23:41 -08:00
doFallback, ok := ConfKey("do_fallback").(bool)
2021-03-02 09:30:59 -08:00
if ok && !doFallback {
return false
}
2021-03-29 09:57:30 -07:00
reason, _ := r.ReadByte()
if reason != uint8(11) && reason != uint8(12) {
2021-03-02 09:30:59 -08:00
return false
}
msg := "shut down"
2021-03-29 09:57:30 -07:00
if reason == uint8(12) {
2021-03-02 09:30:59 -08:00
msg = "crashed"
}
2021-03-09 13:23:41 -08:00
defsrv, ok := ConfKey("default_server").(string)
2021-03-02 09:30:59 -08:00
if !ok {
log.Print("Default server name not set or not a string")
return false
}
if dst.ServerName() == defsrv {
return false
}
dst.SendChatMsg("The minetest server has " + msg + ", connecting you to the default server...")
go dst.Redirect(defsrv)
for src.Forward() {
}
2021-03-10 10:36:37 -08:00
return true
case ToClientMediaPush:
2021-03-29 09:57:30 -07:00
digLenBytes := make([]byte, 2)
r.Read(digLenBytes)
digLen := binary.BigEndian.Uint16(digLenBytes)
digest := make([]byte, digLen)
r.Read(digest)
namelenBytes := make([]byte, 2)
r.Read(namelenBytes)
namelen := binary.BigEndian.Uint16(namelenBytes)
name := make([]byte, namelen)
r.Read(name)
cacheByte, _ := r.ReadByte()
cache := cacheByte == uint8(1)
r.Seek(5, io.SeekCurrent)
data := make([]byte, r.Len())
r.Read(data)
2021-03-10 10:36:37 -08:00
media[string(name)] = &mediaFile{
digest: digest,
data: data,
noCache: !cache,
}
2021-03-29 09:57:30 -07:00
for _, conn := range Conns() {
ack, err := conn.Send(*pkt)
2021-03-10 10:36:37 -08:00
if err != nil {
log.Print(err)
}
<-ack
}
updateMediaCache()
2021-03-02 09:30:59 -08:00
return true
2021-01-14 04:40:31 -08:00
default:
return false
}
} else {
2021-03-29 09:57:30 -07:00
switch cmd := binary.BigEndian.Uint16(cmdBytes); cmd {
2021-01-14 04:40:31 -08:00
case ToServerChatMessage:
2021-01-14 08:09:06 -08:00
return processChatMessage(src, *pkt)
2021-01-16 12:48:21 -08:00
case ToServerFirstSrp:
if src.sudoMode {
src.sudoMode = false
// This is a password change, save verifier and salt
2021-03-29 09:57:30 -07:00
lenSBytes := make([]byte, 2)
r.Read(lenSBytes)
lenS := binary.BigEndian.Uint16(lenSBytes)
s := make([]byte, lenS)
r.Read(s)
lenVBytes := make([]byte, 2)
r.Read(lenVBytes)
lenV := binary.BigEndian.Uint16(lenVBytes)
2021-01-16 12:48:21 -08:00
2021-03-29 09:57:30 -07:00
v := make([]byte, lenV)
r.Read(v)
2021-01-16 12:48:21 -08:00
pwd := encodeVerifierAndSalt(s, v)
db, err := initAuthDB()
if err != nil {
log.Print(err)
return true
}
2021-01-30 03:24:37 -08:00
err = modAuthItem(db, src.Username(), pwd)
2021-01-16 12:48:21 -08:00
if err != nil {
log.Print(err)
return true
}
db.Close()
} else {
2021-01-30 03:24:37 -08:00
log.Print("User " + src.Username() + " at " + src.Addr().String() + " did not enter sudo mode before attempting to change the password")
2021-01-16 12:48:21 -08:00
}
2021-03-29 09:57:30 -07:00
2021-01-16 12:48:21 -08:00
return true
case ToServerSrpBytesA:
if !src.sudoMode {
2021-03-29 09:57:30 -07:00
lenABytes := make([]byte, 2)
r.Read(lenABytes)
lenA := binary.BigEndian.Uint16(lenABytes)
A := make([]byte, lenA)
r.Read(A)
2021-01-16 12:48:21 -08:00
db, err := initAuthDB()
if err != nil {
log.Print(err)
return true
}
2021-01-30 03:24:37 -08:00
pwd, err := readAuthItem(db, src.Username())
2021-01-16 12:48:21 -08:00
if err != nil {
log.Print(err)
return true
}
db.Close()
s, v, err := decodeVerifierAndSalt(pwd)
if err != nil {
log.Print(err)
return true
}
B, _, K, err := srp.Handshake(A, v)
if err != nil {
log.Print(err)
return true
}
src.srp_s = s
src.srp_A = A
src.srp_B = B
src.srp_K = K
// Send SRP_BYTES_S_B
data := make([]byte, 6+len(s)+len(B))
data[0] = uint8(0x00)
data[1] = uint8(ToClientSrpBytesSB)
binary.BigEndian.PutUint16(data[2:4], uint16(len(s)))
copy(data[4:4+len(s)], s)
binary.BigEndian.PutUint16(data[4+len(s):6+len(s)], uint16(len(B)))
copy(data[6+len(s):6+len(s)+len(B)], B)
2021-03-29 09:57:30 -07:00
ack, err := src.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
2021-01-16 12:48:21 -08:00
if err != nil {
log.Print(err)
return true
}
<-ack
}
return true
case ToServerSrpBytesM:
if !src.sudoMode {
2021-03-29 09:57:30 -07:00
lenMBytes := make([]byte, 2)
r.Read(lenMBytes)
lenM := binary.BigEndian.Uint16(lenMBytes)
M := make([]byte, lenM)
r.Read(M)
2021-01-16 12:48:21 -08:00
2021-03-31 10:27:50 -07:00
M2 := srp.ClientProof([]byte(src.Username()), src.srp_s, src.srp_A, src.srp_B, src.srp_K)
2021-01-16 12:48:21 -08:00
if subtle.ConstantTimeCompare(M, M2) == 1 {
// Password is correct
// Enter sudo mode
src.sudoMode = true
// Send ACCEPT_SUDO_MODE
2021-01-19 11:37:35 -08:00
data := []byte{0, ToClientAcceptSudoMode}
2021-01-16 12:48:21 -08:00
2021-03-29 09:57:30 -07:00
ack, err := src.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
2021-01-16 12:48:21 -08:00
if err != nil {
log.Print(err)
return true
}
<-ack
} else {
// Client supplied wrong password
2021-01-30 03:24:37 -08:00
log.Print("User " + src.Username() + " at " + src.Addr().String() + " supplied wrong password for sudo mode")
2021-01-16 12:48:21 -08:00
// Send DENY_SUDO_MODE
2021-01-19 11:37:35 -08:00
data := []byte{0, ToClientDenySudoMode}
2021-01-16 12:48:21 -08:00
2021-03-29 09:57:30 -07:00
ack, err := src.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
2021-01-16 12:48:21 -08:00
if err != nil {
log.Print(err)
return true
}
<-ack
}
}
return true
case ToServerModChannelJoin:
2021-03-20 11:38:57 -07:00
deny := func() {
data := make([]byte, 5+len(rpcCh))
data[0] = uint8(0x00)
data[1] = uint8(ToClientModChannelSignal)
data[2] = uint8(ModChSigJoinFail)
binary.BigEndian.PutUint16(data[3:5], uint16(len(rpcCh)))
copy(data[5:], []byte(rpcCh))
2021-03-29 09:57:30 -07:00
ack, err := src.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
log.Print(err)
}
<-ack
2021-03-20 11:38:57 -07:00
}
chAllowed, ok := ConfKey("modchannels").(bool)
if ok && !chAllowed {
deny()
return true
}
2021-03-29 09:57:30 -07:00
r.Seek(2, io.SeekCurrent)
chBytes := make([]byte, r.Len())
r.Read(chBytes)
ch := string(chBytes)
2021-03-20 11:38:57 -07:00
if ch == rpcCh {
deny()
return true
}
src.modChs[ch] = true
return false
case ToServerModChannelLeave:
2021-03-20 11:38:57 -07:00
deny := func() {
data := make([]byte, 5+len(rpcCh))
data[0] = uint8(0x00)
data[1] = uint8(ToClientModChannelSignal)
data[2] = uint8(ModChSigLeaveFail)
binary.BigEndian.PutUint16(data[3:5], uint16(len(rpcCh)))
copy(data[5:], []byte(rpcCh))
2021-03-29 09:57:30 -07:00
ack, err := src.Send(rudp.Pkt{Reader: bytes.NewReader(data)})
if err != nil {
log.Print(err)
}
<-ack
2021-03-20 11:38:57 -07:00
}
2021-03-20 11:38:57 -07:00
chAllowed, ok := ConfKey("modchannels").(bool)
if ok && !chAllowed {
deny()
return true
}
2021-03-29 09:57:30 -07:00
r.Seek(2, io.SeekCurrent)
chBytes := make([]byte, r.Len())
r.Read(chBytes)
ch := string(chBytes)
2021-03-20 11:38:57 -07:00
if ch == rpcCh {
deny()
return true
}
src.modChs[ch] = false
return false
case ToServerModChannelMsg:
2021-03-20 11:38:57 -07:00
chAllowed, ok := ConfKey("modchannels").(bool)
if ok && !chAllowed {
return true
}
2021-03-29 09:57:30 -07:00
chlenBytes := make([]byte, 2)
r.Read(chlenBytes)
chlen := binary.BigEndian.Uint16(chlenBytes)
chBytes := make([]byte, chlen)
r.Read(chBytes)
ch := string(chBytes)
if ch == rpcCh {
return true
}
return false
2021-01-14 04:40:31 -08:00
default:
return false
}
}
2021-01-16 12:48:21 -08:00
return false
2021-01-14 04:40:31 -08:00
}