master
anon5 2021-01-09 11:26:30 +00:00
parent 8391275b5e
commit 9b12ce30c4
22 changed files with 639 additions and 639 deletions

View File

@ -13,38 +13,38 @@ func processAORmAdd(p *Peer, data []byte) {
aoRm := make([]uint16, countRm)
aoRmI := 0
for i := uint16(0); i < countRm; i += 2 {
aoRm[aoRmI] = binary.BigEndian.Uint16(data[4 + i:6 + i])
aoRm[aoRmI] = binary.BigEndian.Uint16(data[4+i : 6+i])
aoRmI++
}
countAdd := binary.BigEndian.Uint16(data[4 + countRm * 2:6 + countRm * 2])
countAdd := binary.BigEndian.Uint16(data[4+countRm*2 : 6+countRm*2])
aoAdd := make([]uint16, countAdd)
aoAddI := 0
j := uint32(0)
for i := uint32(0); i < uint32(countAdd); i++ {
si := j + 6 + uint32(countRm) * 2
initDataLen := binary.BigEndian.Uint32(data[3 + si:7 + si])
if data[2 + si] == uint8(0x65) && !p.initAoReceived {
si := j + 6 + uint32(countRm)*2
initDataLen := binary.BigEndian.Uint32(data[3+si : 7+si])
if data[2+si] == uint8(0x65) && !p.initAoReceived {
p.initAoReceived = true
j += 7 + initDataLen
continue
}
aoAdd[aoAddI] = binary.BigEndian.Uint16(data[si:2 + si])
aoAdd[aoAddI] = binary.BigEndian.Uint16(data[si : 2+si])
aoAddI++
j += 7 + initDataLen
}
for i := range aoAdd {
if aoAdd[i] != 0 {
aoIDs[p.ID()][aoAdd[i]] = true
}
}
for i := range aoRm {
aoIDs[p.ID()][aoRm[i]] = false
}

View File

@ -3,7 +3,7 @@ package multiserver
import (
"io/ioutil"
"strings"
"gopkg.in/yaml.v2"
)
@ -15,14 +15,14 @@ func LoadConfig() error {
if err != nil {
return err
}
Config = make(map[interface{}]interface{})
err = yaml.Unmarshal(data, &Config)
if err != nil {
return err
}
return nil
}
@ -30,12 +30,12 @@ func LoadConfig() error {
func GetConfKey(key string) interface{} {
keys := strings.Split(key, ":")
c := Config
for i := 0; i < len(keys) - 1; i++ {
for i := 0; i < len(keys)-1; i++ {
if c[keys[i]] == nil {
return nil
}
c = c[keys[i]].(map[interface{}]interface{})
}
return c[keys[len(keys) - 1]]
return c[keys[len(keys)-1]]
}

16
end.go
View File

@ -2,15 +2,15 @@ package multiserver
import (
"log"
"time"
"os"
"time"
)
func End(crash, reconnect bool) {
log.Print("Ending")
l := GetListener()
data := make([]byte, 7)
data[0] = uint8(0x00)
data[1] = uint8(0x0A)
@ -27,7 +27,7 @@ func End(crash, reconnect bool) {
data[5] = uint8(0x00)
}
data[6] = uint8(0x00)
i := PeerIDCltMin
for l.id2peer[i].Peer != nil {
ack, err := l.id2peer[i].Send(Pkt{Data: data, ChNo: 0, Unrel: false})
@ -35,15 +35,15 @@ func End(crash, reconnect bool) {
log.Print(err)
}
<-ack
l.id2peer[i].SendDisco(0, true)
l.id2peer[i].Close()
i++
}
time.Sleep(time.Second)
if crash {
os.Exit(1)
} else {

View File

@ -1,11 +1,11 @@
package multiserver
import (
"strings"
"encoding/binary"
"time"
"log"
"strings"
"time"
"github.com/yuin/gopher-lua"
)
@ -21,7 +21,7 @@ var chatMessageHandlers []*lua.LFunction
func registerChatCommand(L *lua.LState) int {
name := L.ToString(1)
cmddef := L.ToTable(2)
privs := cmddef.RawGet(lua.LString("privs")).(*lua.LTable)
pmap := make(map[string]bool)
privs.ForEach(func(k, v lua.LValue) {
@ -29,18 +29,18 @@ func registerChatCommand(L *lua.LState) int {
pmap[k.String()] = true
}
})
f := cmddef.RawGet(lua.LString("func")).(*lua.LFunction)
chatCommands = append(chatCommands, chatCommand{name: name, privs: pmap, function: f})
return 0
}
func registerOnChatMessage(L *lua.LState) int {
f := L.ToFunction(1)
chatMessageHandlers = append(chatMessageHandlers, f)
return 0
}
@ -58,29 +58,29 @@ func processChatMessage(peerid PeerID, msg []byte) bool {
log.Print(err)
return true
}
eprivs, err := readPrivItem(db, string(GetListener().GetPeerByID(peerid).username))
if err != nil {
log.Print(err)
return true
}
db.Close()
privs := decodePrivs(eprivs)
allowAccess := true
for priv := range chatCommands[i].privs {
if chatCommands[i].privs[priv] && !privs[priv] {
allowAccess = false
}
}
if !allowAccess {
str := "You do not have permission to run this command! Required privileges: " + strings.Replace(encodePrivs(chatCommands[i].privs), "|", " ", -1)
wstr := wider([]byte(str))
data := make([]byte, 16 + len(wstr))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(0x2F)
data[2] = uint8(0x01)
@ -88,34 +88,34 @@ func processChatMessage(peerid PeerID, msg []byte) bool {
data[4] = uint8(0x00)
data[5] = uint8(0x00)
binary.BigEndian.PutUint16(data[6:8], uint16(len(str)))
copy(data[8:8 + len(wstr)], wstr)
data[8 + len(wstr)] = uint8(0x00)
data[9 + len(wstr)] = uint8(0x00)
data[10 + len(wstr)] = uint8(0x00)
data[11 + len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12 + len(wstr):16 + len(wstr)], uint32(time.Now().Unix()))
copy(data[8:8+len(wstr)], wstr)
data[8+len(wstr)] = uint8(0x00)
data[9+len(wstr)] = uint8(0x00)
data[10+len(wstr)] = uint8(0x00)
data[11+len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12+len(wstr):16+len(wstr)], uint32(time.Now().Unix()))
ack, err := GetListener().GetPeerByID(peerid).Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
}
<-ack
return true
}
// Callback
if err := l.CallByParam(lua.P{Fn: chatCommands[i].function, NRet: 1, Protect: true}, lua.LNumber(peerid), lua.LString(strings.Join(params[1:], " "))); err != nil {
log.Print(err)
go func() {
End(true, true)
}()
}
if str, ok := l.Get(-1).(lua.LString); ok {
wstr := wider([]byte(str.String()))
data := make([]byte, 16 + len(wstr))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(0x2F)
data[2] = uint8(0x01)
@ -123,20 +123,20 @@ func processChatMessage(peerid PeerID, msg []byte) bool {
data[4] = uint8(0x00)
data[5] = uint8(0x00)
binary.BigEndian.PutUint16(data[6:8], uint16(len(str.String())))
copy(data[8:8 + len(wstr)], wstr)
data[8 + len(wstr)] = uint8(0x00)
data[9 + len(wstr)] = uint8(0x00)
data[10 + len(wstr)] = uint8(0x00)
data[11 + len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12 + len(wstr):16 + len(wstr)], uint32(time.Now().Unix()))
copy(data[8:8+len(wstr)], wstr)
data[8+len(wstr)] = uint8(0x00)
data[9+len(wstr)] = uint8(0x00)
data[10+len(wstr)] = uint8(0x00)
data[11+len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12+len(wstr):16+len(wstr)], uint32(time.Now().Unix()))
ack, err := GetListener().GetPeerByID(peerid).Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
}
<-ack
}
return true
}
}
@ -145,7 +145,7 @@ func processChatMessage(peerid PeerID, msg []byte) bool {
for i := range chatMessageHandlers {
if err := l.CallByParam(lua.P{Fn: chatMessageHandlers[i], NRet: 1, Protect: true}, lua.LNumber(peerid), lua.LString(s)); err != nil {
log.Print(err)
End(true, true)
}
if b, ok := l.Get(-1).(lua.LBool); ok {
@ -155,7 +155,7 @@ func processChatMessage(peerid PeerID, msg []byte) bool {
}
}
}
return false
}
@ -164,10 +164,10 @@ func chatSendPlayer(L *lua.LState) int {
msg := L.ToString(2)
l := GetListener()
p := l.GetPeerByID(id)
wstr := wider([]byte(msg))
data := make([]byte, 16 + len(wstr))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(0x2F)
data[2] = uint8(0x01)
@ -175,30 +175,30 @@ func chatSendPlayer(L *lua.LState) int {
data[4] = uint8(0x00)
data[5] = uint8(0x00)
binary.BigEndian.PutUint16(data[6:8], uint16(len(msg)))
copy(data[8:8 + len(wstr)], wstr)
data[8 + len(wstr)] = uint8(0x00)
data[9 + len(wstr)] = uint8(0x00)
data[10 + len(wstr)] = uint8(0x00)
data[11 + len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12 + len(wstr):16 + len(wstr)], uint32(time.Now().Unix()))
copy(data[8:8+len(wstr)], wstr)
data[8+len(wstr)] = uint8(0x00)
data[9+len(wstr)] = uint8(0x00)
data[10+len(wstr)] = uint8(0x00)
data[11+len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12+len(wstr):16+len(wstr)], uint32(time.Now().Unix()))
ack, err := p.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
return 0
}
<-ack
return 0
}
func chatSendAll(L *lua.LState) int {
msg := L.ToString(1)
l := GetListener()
wstr := wider([]byte(msg))
data := make([]byte, 16 + len(wstr))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(0x2F)
data[2] = uint8(0x01)
@ -206,13 +206,13 @@ func chatSendAll(L *lua.LState) int {
data[4] = uint8(0x00)
data[5] = uint8(0x00)
binary.BigEndian.PutUint16(data[6:8], uint16(len(msg)))
copy(data[8:8 + len(wstr)], wstr)
data[8 + len(wstr)] = uint8(0x00)
data[9 + len(wstr)] = uint8(0x00)
data[10 + len(wstr)] = uint8(0x00)
data[11 + len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12 + len(wstr):16 + len(wstr)], uint32(time.Now().Unix()))
copy(data[8:8+len(wstr)], wstr)
data[8+len(wstr)] = uint8(0x00)
data[9+len(wstr)] = uint8(0x00)
data[10+len(wstr)] = uint8(0x00)
data[11+len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12+len(wstr):16+len(wstr)], uint32(time.Now().Unix()))
i := PeerIDCltMin
for l.id2peer[i].Peer != nil {
ack, err := l.id2peer[i].Send(Pkt{Data: data, ChNo: 0, Unrel: false})
@ -221,10 +221,10 @@ func chatSendAll(L *lua.LState) int {
return 0
}
<-ack
i++
}
return 0
}
@ -235,7 +235,7 @@ func narrow(b []byte) []byte {
r = append(r, b[i])
}
}
return r
}
@ -244,6 +244,6 @@ func wider(b []byte) []byte {
for i := range b {
r = append(r, uint8(0x00), b[i])
}
return r
}

View File

@ -6,14 +6,14 @@ import (
func luaGetConfKey(L *lua.LState) int {
key := L.ToString(1)
v := GetConfKey(key)
if v == nil {
L.Push(lua.LNil)
} else {
L.Push(lua.LString(v.(string)))
}
return 1
}

View File

@ -4,10 +4,10 @@ import "github.com/yuin/gopher-lua"
func luaEnd(L *lua.LState) int {
reconnect := L.ToBool(1)
go func() {
End(false, reconnect)
}()
return 0
}

View File

@ -2,13 +2,13 @@ package multiserver
import (
"log"
"github.com/yuin/gopher-lua"
)
func luaLog(L *lua.LState) int {
str := L.ToString(1)
log.Print(str)
return 0
}

View File

@ -3,24 +3,24 @@ package multiserver
import (
"encoding/binary"
"log"
"github.com/yuin/gopher-lua"
)
var joinHandlers []*lua.LFunction
var joinHandlers []*lua.LFunction
var leaveHandlers []*lua.LFunction
func registerOnJoinPlayer(L *lua.LState) int {
f := L.ToFunction(1)
joinHandlers = append(joinHandlers, f)
return 0
}
func registerOnLeavePlayer(L *lua.LState) int {
f := L.ToFunction(1)
leaveHandlers = append(leaveHandlers, f)
return 0
}
@ -28,7 +28,7 @@ func processJoin(peerid PeerID) {
for i := range joinHandlers {
if err := l.CallByParam(lua.P{Fn: joinHandlers[i], NRet: 0, Protect: true}, lua.LNumber(peerid)); err != nil {
log.Print(err)
End(true, true)
}
}
@ -38,7 +38,7 @@ func processLeave(peerid PeerID) {
for i := range leaveHandlers {
if err := l.CallByParam(lua.P{Fn: leaveHandlers[i], NRet: 0, Protect: true}, lua.LNumber(peerid)); err != nil {
log.Print(err)
End(true, true)
}
}
@ -48,38 +48,38 @@ func getPlayerName(L *lua.LState) int {
id := L.ToInt(1)
l := GetListener()
p := l.GetPeerByID(PeerID(id))
if p != nil {
L.Push(lua.LString(p.username))
} else {
L.Push(lua.LNil)
}
return 1
}
func luaGetPeerID(L *lua.LState) int {
name := L.ToString(1)
l := GetListener()
found := false
i := PeerIDCltMin
for l.id2peer[i].Peer != nil {
if string(l.id2peer[i].username) == name {
found = true
L.Push(lua.LNumber(i))
break
}
i++
}
if !found {
L.Push(lua.LNil)
}
return 1
}
@ -88,32 +88,32 @@ func kickPlayer(L *lua.LState) int {
reason := L.ToString(2)
l := GetListener()
p := l.GetPeerByID(PeerID(id))
if reason == "" {
reason = "Kicked."
} else {
reason = "Kicked. " + reason
}
msg := []byte(reason)
data := make([]byte, 6 + len(msg))
data := make([]byte, 6+len(msg))
data[0] = uint8(0x00)
data[1] = uint8(0x0A)
data[2] = uint8(0x0A)
binary.BigEndian.PutUint16(data[3:5], uint16(len(msg)))
copy(data[5:5 + len(msg)], msg)
data[5 + len(msg)] = uint8(0x00)
copy(data[5:5+len(msg)], msg)
data[5+len(msg)] = uint8(0x00)
ack, err := p.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
}
<-ack
p.SendDisco(0, true)
p.Close()
return 0
}
@ -121,16 +121,16 @@ func getCurrentServer(L *lua.LState) int {
id := L.ToInt(1)
l := GetListener()
p := l.GetPeerByID(PeerID(id))
servers := GetConfKey("servers").(map[interface{}]interface{})
for server := range servers {
if GetConfKey("servers:" + server.(string) + ":address") == p.Server().Addr().String() {
if GetConfKey("servers:"+server.(string)+":address") == p.Server().Addr().String() {
L.Push(lua.LString(server.(string)))
break
}
}
return 1
}
@ -138,28 +138,28 @@ func getPlayerAddress(L *lua.LState) int {
id := L.ToInt(1)
l := GetListener()
p := l.GetPeerByID(PeerID(id))
if p != nil {
L.Push(lua.LString(p.Addr().String()))
} else {
L.Push(lua.LNil)
}
return 1
}
func getConnectedPlayers(L *lua.LState) int {
l := GetListener()
r := L.NewTable()
i := PeerIDCltMin
for l.id2peer[i].Peer != nil {
r.Append(lua.LNumber(i))
i++
}
L.Push(r)
return 1
}

View File

@ -2,105 +2,105 @@ package multiserver
import (
"log"
"github.com/yuin/gopher-lua"
)
func getPlayerPrivs(L *lua.LState) int {
name := L.ToString(1)
r := L.NewTable()
db, err := initDB()
if err != nil {
log.Print(err)
L.Push(lua.LNil)
return 1
}
eprivs, err := readPrivItem(db, name)
if err != nil {
log.Print(err)
L.Push(lua.LNil)
return 1
}
db.Close()
privs := decodePrivs(eprivs)
for priv := range privs {
if privs[priv] {
r.RawSet(lua.LString(priv), lua.LBool(true))
}
}
L.Push(r)
return 1
}
func setPlayerPrivs(L *lua.LState) int {
name := L.ToString(1)
newprivs := L.ToTable(2)
newpmap := make(map[string]bool)
newprivs.ForEach(func(k, v lua.LValue) {
if lua.LVAsBool(v) {
newpmap[k.String()] = true
}
})
ps := encodePrivs(newpmap)
db, err := initDB()
if err != nil {
log.Print(err)
return 0
}
err = modPrivItem(db, name, ps)
if err != nil {
log.Print(err)
return 0
}
return 0
}
func checkPlayerPrivs(L *lua.LState) int {
name := L.ToString(1)
reqprivs := L.ToTable(2)
db, err := initDB()
if err != nil {
log.Print(err)
L.Push(lua.LBool(false))
return 1
}
eprivs, err := readPrivItem(db, name)
if err != nil {
log.Print(err)
L.Push(lua.LBool(false))
return 1
}
db.Close()
privs := decodePrivs(eprivs)
hasPrivs := true
reqprivs.ForEach(func(k, v lua.LValue) {
if lua.LVAsBool(v) {
@ -109,12 +109,12 @@ func checkPlayerPrivs(L *lua.LState) int {
}
}
})
if hasPrivs {
L.Push(lua.LBool(true))
} else {
L.Push(lua.LBool(false))
}
return 1
}

View File

@ -2,7 +2,7 @@ package multiserver
import (
"log"
"github.com/yuin/gopher-lua"
)
@ -11,28 +11,28 @@ var redirectDoneHandlers []*lua.LFunction
func registerOnRedirectDone(L *lua.LState) int {
f := L.ToFunction(1)
redirectDoneHandlers = append(redirectDoneHandlers, f)
return 0
}
func processRedirectDone(p *Peer, newsrv string) {
var srv string
servers := GetConfKey("servers").(map[interface{}]interface{})
for server := range servers {
if GetConfKey("servers:" + server.(string) + ":address") == p.Server().Addr().String() {
if GetConfKey("servers:"+server.(string)+":address") == p.Server().Addr().String() {
srv = server.(string)
break
}
}
success := srv == newsrv
for i := range redirectDoneHandlers {
if err := l.CallByParam(lua.P{Fn: redirectDoneHandlers[i], NRet: 0, Protect: true}, lua.LNumber(p.ID()), lua.LString(newsrv), lua.LBool(success)); err != nil {
log.Print(err)
End(true, true)
}
}
@ -43,22 +43,22 @@ func redirect(L *lua.LState) int {
srv := L.ToString(2)
l := GetListener()
p := l.GetPeerByID(id)
go p.Redirect(srv)
return 0
}
func getServers(L *lua.LState) int {
servers := GetConfKey("servers").(map[interface{}]interface{})
r := L.NewTable()
for server := range servers {
addr := GetConfKey("servers:" + server.(string) + ":address")
r.RawSet(lua.LString(server.(string)), lua.LString(addr.(string)))
}
L.Push(r)
return 1
}

View File

@ -2,21 +2,21 @@ package multiserver
import (
"strings"
"github.com/yuin/gopher-lua"
)
func luaStringSplit(L *lua.LState) int {
s := L.ToString(1)
d := L.ToString(2)
split := strings.Split(s, d)
r := L.NewTable()
for i := range split {
r.Append(lua.LString(split[i]))
}
L.Push(r)
return 1
}

View File

@ -1,25 +1,25 @@
package multiserver
import (
"encoding/binary"
"errors"
"fmt"
"net"
"sync"
"errors"
"encoding/binary"
"fmt"
)
var ErrPlayerLimitReached = errors.New("player limit reached")
type Listener struct {
conn net.PacketConn
clts chan cltPeer
errs chan error
mu sync.Mutex
mu sync.Mutex
addr2peer map[string]cltPeer
id2peer map[PeerID]cltPeer
peerid PeerID
id2peer map[PeerID]cltPeer
peerid PeerID
}
var listener *Listener
@ -28,14 +28,14 @@ var listener *Listener
func Listen(conn net.PacketConn) *Listener {
l := &Listener{
conn: conn,
clts: make(chan cltPeer),
errs: make(chan error),
addr2peer: make(map[string]cltPeer),
id2peer: make(map[PeerID]cltPeer),
}
pkts := make(chan netPkt)
go readNetPkts(l.conn, pkts, l.errs)
go func() {
@ -44,14 +44,14 @@ func Listen(conn net.PacketConn) *Listener {
l.errs <- err
}
}
close(l.clts)
for _, clt := range l.addr2peer {
clt.Close()
}
}()
return l
}
@ -70,9 +70,9 @@ func (l *Listener) Accept() (*Peer, error) {
}
}
close(clt.accepted)
connectedPeers++
return clt.Peer, nil
case err := <-l.errs:
return nil, err
@ -86,81 +86,81 @@ var ErrOutOfPeerIDs = errors.New("out of peer ids")
type cltPeer struct {
*Peer
pkts chan<- netPkt
pkts chan<- netPkt
accepted chan struct{} // close-only
}
func (l *Listener) processNetPkt(pkt netPkt) error {
l.mu.Lock()
defer l.mu.Unlock()
addrstr := pkt.SrcAddr.String()
clt, ok := l.addr2peer[addrstr]
if !ok {
prev := l.peerid
for l.id2peer[l.peerid].Peer != nil || l.peerid < PeerIDCltMin {
if l.peerid == prev - 1 {
if l.peerid == prev-1 {
return ErrOutOfPeerIDs
}
l.peerid++
}
pkts := make(chan netPkt, 256)
clt = cltPeer{
Peer: newPeer(l.conn, pkt.SrcAddr, l.peerid, PeerIDSrv),
pkts: pkts,
Peer: newPeer(l.conn, pkt.SrcAddr, l.peerid, PeerIDSrv),
pkts: pkts,
accepted: make(chan struct{}),
}
l.addr2peer[addrstr] = clt
l.id2peer[clt.ID()] = clt
data := make([]byte, 2 + 2)
data := make([]byte, 2+2)
data[0] = uint8(rawTypeCtl)
data[1] = uint8(ctlSetPeerID)
binary.BigEndian.PutUint16(data[2:4], uint16(clt.ID()))
if _, err := clt.sendRaw(rawPkt{Data: data}); err != nil {
return fmt.Errorf("can't set client peer id: %w", err)
}
var maxPeers int
maxPeersKey := GetConfKey("player_limit")
if maxPeersKey == nil || fmt.Sprintf("%T", maxPeersKey) != "int" {
maxPeers = -1
}
maxPeers = maxPeersKey.(int)
if GetPeerCount() >= maxPeers && maxPeers > -1 {
data := []byte{
uint8(0x00), uint8(0x0A),
uint8(0x06), uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
}
_, err := clt.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
return err
}
clt.SendDisco(0, true)
clt.Close()
return ErrPlayerLimitReached
}
go func() {
select {
case l.clts <- clt:
case <-clt.Disco():
}
clt.processNetPkts(pkts)
}()
go func() {
<-clt.Disco()
l.mu.Lock()
close(pkts)
delete(l.addr2peer, addrstr)
@ -168,7 +168,7 @@ func (l *Listener) processNetPkt(pkt netPkt) error {
l.mu.Unlock()
}()
}
select {
case <-clt.accepted:
clt.pkts <- pkt
@ -179,7 +179,7 @@ func (l *Listener) processNetPkt(pkt netPkt) error {
return fmt.Errorf("ignoring net pkt from %s because buf is full", addrstr)
}
}
return nil
}

4
lua.go
View File

@ -7,10 +7,10 @@ var api_funcs *lua.LTable
func InitLua() {
l = lua.NewState()
api_funcs = l.NewTable()
l.SetGlobal("multiserver", api_funcs)
// redirect
addLuaFunc(redirect, "redirect")
addLuaFunc(getServers, "get_servers")

View File

@ -1,55 +1,55 @@
package main
import (
"net"
"log"
"fmt"
"log"
"net"
"time"
"github.com/HimbeerserverDE/multiserver"
)
func main() {
multiserver.InitAOMap()
multiserver.LoadConfig()
multiserver.InitLua()
defer multiserver.CloseLua()
err := multiserver.LoadPlugins()
if err != nil {
log.Fatal(err)
return
}
lobbyaddr := multiserver.GetConfKey("servers:lobby:address")
if lobbyaddr == nil || fmt.Sprintf("%T", lobbyaddr) != "string" {
log.Fatal("Lobby server address not set or not a string")
return
}
host := multiserver.GetConfKey("host")
if host == nil || fmt.Sprintf("%T", host) != "string" {
log.Fatal("Host not set or not a string")
return
}
srvaddr, err := net.ResolveUDPAddr("udp", lobbyaddr.(string))
if err != nil {
log.Fatal(err)
return
}
lc, err := net.ListenPacket("udp", host.(string))
if err != nil {
log.Fatal(err)
return
}
defer lc.Close()
log.Print("Listening on " + host.(string))
l := multiserver.Listen(lc)
multiserver.SetListener(l)
for {
@ -58,43 +58,43 @@ func main() {
log.Print(err)
continue
}
log.Print(clt.Addr(), " connected")
conn, err := net.DialUDP("udp", nil, srvaddr)
if err != nil {
log.Fatal(err)
return
}
srv := multiserver.Connect(conn, conn.RemoteAddr())
if srv == nil {
data := []byte{
uint8(0x00), uint8(0x0A),
uint8(0x09), uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
}
_, err := clt.Send(multiserver.Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
}
time.Sleep(250 * time.Millisecond)
clt.SendDisco(0, true)
clt.Close()
continue
}
fin := make(chan struct{}) // close-only
go multiserver.Init(srv, clt, false, fin)
go func() {
<-fin
clt.SetServer(srv)
go multiserver.Proxy(clt, srv)
go multiserver.Proxy(srv, clt)
}()

12
net.go
View File

@ -1,8 +1,8 @@
package multiserver
import (
"net"
"errors"
"net"
"strings"
)
@ -17,8 +17,8 @@ netPkt.Data format (big endian):
RawPkt.Data
*/
type netPkt struct {
SrcAddr net.Addr
Data []byte
SrcAddr net.Addr
Data []byte
}
func readNetPkts(conn net.PacketConn, pkts chan<- netPkt, errs chan<- error) {
@ -29,13 +29,13 @@ func readNetPkts(conn net.PacketConn, pkts chan<- netPkt, errs chan<- error) {
if strings.Contains(err.Error(), "use of closed network connection") {
break
}
errs <- err
continue
}
pkts <- netPkt{addr, buf[:n]}
}
close(pkts)
}

412
peer.go
View File

@ -1,18 +1,18 @@
package multiserver
import (
"time"
"net"
"sync"
"crypto/subtle"
"database/sql"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"log"
"errors"
"net"
"strings"
"encoding/binary"
"encoding/base64"
"database/sql"
"crypto/subtle"
"sync"
"time"
"github.com/HimbeerserverDE/srp"
_ "github.com/mattn/go-sqlite3"
)
@ -30,7 +30,7 @@ const (
// ConnTimeout is the amount of time after no packets being received
// from a Peer that it is automatically disconnected
ConnTimeout = 30 * time.Second
// PingTimeout is the amount of time after no packets being sent
// to a Peer that a CtlPing is automatically sent to prevent timeout
PingTimeout = 5 * time.Second
@ -45,49 +45,49 @@ const (
type Peer struct {
conn net.PacketConn
addr net.Addr
disco chan struct{} // close-only
id PeerID
pkts chan Pkt
errs chan error // don't close
pkts chan Pkt
errs chan error // don't close
timedout chan struct{} // close only
chans [ChannelCount]pktchan // read/write
mu sync.RWMutex
mu sync.RWMutex
idOfPeer PeerID
timeout *time.Timer
ping *time.Ticker
timeout *time.Timer
ping *time.Ticker
username []byte
srp_s []byte
srp_A []byte
srp_a []byte
srp_B []byte
srp_K []byte
authMech int
forward bool
srv *Peer
initAoReceived bool
}
type pktchan struct {
insplit map[seqnum][][]byte
inrel map[seqnum][]byte
inrelsn seqnum
insplit map[seqnum][][]byte
inrel map[seqnum][]byte
inrelsn seqnum
ackchans sync.Map // map[seqnum]chan struct{}
outsplitmu sync.Mutex
outsplitsn seqnum
outrelmu sync.Mutex
outrelsn seqnum
outrelwin seqnum
@ -161,70 +161,70 @@ func (p *Peer) Recv() (Pkt, error) {
func (p *Peer) Close() error {
p.mu.Lock()
defer p.mu.Unlock()
select {
case <-p.Disco():
return ErrClosed
default:
}
p.timeout.Stop()
p.timeout = nil
p.ping.Stop()
p.ping = nil
close(p.disco)
return nil
}
func newPeer(conn net.PacketConn, addr net.Addr, id, idOfPeer PeerID) *Peer {
p := &Peer{
conn: conn,
addr: addr,
id: id,
conn: conn,
addr: addr,
id: id,
idOfPeer: idOfPeer,
pkts: make(chan Pkt),
disco: make(chan struct{}),
errs: make(chan error),
pkts: make(chan Pkt),
disco: make(chan struct{}),
errs: make(chan error),
}
for i := range p.chans {
p.chans[i] = pktchan{
insplit: make(map[seqnum][][]byte),
inrel: make(map[seqnum][]byte),
inrelsn: seqnumInit,
outsplitsn: seqnumInit,
outrelsn: seqnumInit,
outrelwin: seqnumInit,
insplit: make(map[seqnum][][]byte),
inrel: make(map[seqnum][]byte),
inrelsn: seqnumInit,
outsplitsn: seqnumInit,
outrelsn: seqnumInit,
outrelwin: seqnumInit,
}
}
p.timedout = make(chan struct{})
p.timeout = time.AfterFunc(ConnTimeout, func() {
close(p.timedout)
p.SendDisco(0, true)
p.Close()
})
p.ping = time.NewTicker(PingTimeout)
go p.sendPings(p.ping.C)
p.forward = true
if !p.IsSrv() {
aoIDs[p.ID()] = make(map[uint16]bool)
}
return p
}
func (p *Peer) sendPings(ping <-chan time.Time) {
pkt := rawPkt{Data: []byte{uint8(rawTypeCtl), uint8(ctlPing)}}
for {
select {
case <-ping:
@ -241,26 +241,26 @@ func (p *Peer) sendPings(ping <-chan time.Time) {
// and closes conn when the Peer disconnects
func Connect(conn net.PacketConn, addr net.Addr) *Peer {
srv := newPeer(conn, addr, PeerIDSrv, PeerIDNil)
pkts := make(chan netPkt)
go readNetPkts(conn, pkts, srv.errs)
go srv.processNetPkts(pkts)
ack, err := srv.Send(Pkt{Data: []byte{uint8(0), uint8(0)}, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
}
t := time.Now()
for time.Since(t).Seconds() < 8 {
breakloop := false
select {
case <-ack:
breakloop = true
default:
}
if breakloop {
break
}
@ -268,19 +268,19 @@ func Connect(conn net.PacketConn, addr net.Addr) *Peer {
if time.Since(t).Seconds() >= 8 {
srv.SendDisco(0, true)
srv.Close()
conn.Close()
return nil
}
srv.sendAck(0, true, 65500)
go func() {
<-srv.Disco()
conn.Close()
}()
return srv
}
@ -289,11 +289,11 @@ func Connect(conn net.PacketConn, addr net.Addr) *Peer {
// This doesn't support AUTH_MECHANISM_FIRST_SRP yet
func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
defer close(fin)
if p2.ID() == PeerIDSrv {
// We're trying to connect to a server
// INIT
data := make([]byte, 11 + len(p.username))
data := make([]byte, 11+len(p.username))
data[0] = uint8(0x00)
data[1] = uint8(0x02)
data[2] = uint8(0x1c)
@ -302,13 +302,13 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
binary.BigEndian.PutUint16(data[7:9], uint16(0x0027))
binary.BigEndian.PutUint16(data[9:11], uint16(len(p.username)))
copy(data[11:], p.username)
time.Sleep(250 * time.Millisecond)
if _, err := p2.Send(Pkt{Data: data, ChNo: 1, Unrel: true}); err != nil {
log.Print(err)
}
for {
pkt, err := p2.Recv()
if err != nil {
@ -318,45 +318,45 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
msg += " (timed out)"
}
log.Print(msg)
if !p2.IsSrv() {
connectedPeers--
processLeave(p2.ID())
}
return
}
log.Print(err)
continue
}
switch cmd := binary.BigEndian.Uint16(pkt.Data[0:2]); cmd {
case 0x02:
if pkt.Data[10] & 2 > 0 {
if pkt.Data[10]&2 > 0 {
// Compute and send SRP_BYTES_A
_, _, err := srp.NewClient([]byte(strings.ToLower(string(p.username))), passPhrase)
if err != nil {
log.Print(err)
continue
}
A, a, err := srp.InitiateHandshake()
if err != nil {
log.Print(err)
continue
}
p.srp_A = A
p.srp_a = a
data := make([]byte, 5 + len(p.srp_A))
data := make([]byte, 5+len(p.srp_A))
data[0] = uint8(0x00)
data[1] = uint8(0x51)
binary.BigEndian.PutUint16(data[2:4], uint16(len(p.srp_A)))
copy(data[4:4 + len(p.srp_A)], p.srp_A)
data[4 + len(p.srp_A)] = uint8(1)
copy(data[4:4+len(p.srp_A)], p.srp_A)
data[4+len(p.srp_A)] = uint8(1)
ack, err := p2.Send(Pkt{Data: data, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
@ -370,16 +370,16 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
log.Print(err)
continue
}
data := make([]byte, 7 + len(s) + len(v))
data := make([]byte, 7+len(s)+len(v))
data[0] = uint8(0x00)
data[1] = uint8(0x50)
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(v)))
copy(data[6 + len(s):6 + len(s) + len(v)], v)
data[6 + len(s) + len(v)] = uint8(0)
copy(data[4:4+len(s)], s)
binary.BigEndian.PutUint16(data[4+len(s):6+len(s)], uint16(len(v)))
copy(data[6+len(s):6+len(s)+len(v)], v)
data[6+len(s)+len(v)] = uint8(0)
ack, err := p2.Send(Pkt{Data: data, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
@ -390,25 +390,25 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
case 0x60:
// Compute and send SRP_BYTES_M
lenS := binary.BigEndian.Uint16(pkt.Data[2:4])
s := pkt.Data[4:lenS + 4]
B := pkt.Data[lenS + 6:]
s := pkt.Data[4 : lenS+4]
B := pkt.Data[lenS+6:]
K, err := srp.CompleteHandshake(p.srp_A, p.srp_a, []byte(strings.ToLower(string(p.username))), passPhrase, s, B)
if err != nil {
log.Print(err)
continue
}
p.srp_K = K
M := srp.CalculateM(p.username, s, p.srp_A, B, p.srp_K)
data := make([]byte, 4 + len(M))
data := make([]byte, 4+len(M))
data[0] = uint8(0x00)
data[1] = uint8(0x52)
binary.BigEndian.PutUint16(data[2:4], uint16(len(M)))
copy(data[4:], M)
ack, err := p2.Send(Pkt{Data: data, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
@ -418,18 +418,18 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
case 0x0A:
// Auth failed for some reason
log.Print(ErrAuthFailed)
data := []byte{
uint8(0x00), uint8(0x0A),
uint8(0x09), uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
}
ack, err := p.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
}
<-ack
p.SendDisco(0, true)
p.Close()
return
@ -441,7 +441,7 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
continue
}
<-ack
if !ignMedia {
return
}
@ -450,21 +450,21 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
if !ignMedia {
continue
}
v := []byte("5.4.0-dev-dd5a732fa")
data := make([]byte, 8 + len(v))
data := make([]byte, 8+len(v))
copy(data[0:6], []byte{uint8(0), uint8(0x43), uint8(5), uint8(4), uint8(0), uint8(0)})
binary.BigEndian.PutUint16(data[6:8], uint16(len(v)))
copy(data[8:], v)
ack, err := p2.Send(Pkt{Data: data, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
return
}
}
@ -478,64 +478,64 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
msg += " (timed out)"
}
log.Print(msg)
if !p2.IsSrv() {
connectedPeers--
processLeave(p2.ID())
}
return
}
log.Print(err)
continue
}
switch cmd := binary.BigEndian.Uint16(pkt.Data[0:2]); cmd {
case 0x02:
// Process data
p2.username = pkt.Data[11:]
// Lua Callback
processJoin(p2.ID())
// Send HELLO
data := make([]byte, 13 + len(p2.username))
data := make([]byte, 13+len(p2.username))
data[0] = uint8(0x00)
data[1] = uint8(0x02)
data[2] = uint8(0x1c)
binary.BigEndian.PutUint16(data[3:5], uint16(0x0000))
binary.BigEndian.PutUint16(data[5:7], uint16(0x0027))
db, err := initDB()
if err != nil {
log.Print(err)
continue
}
pwd, err := readAuthItem(db, string(p2.username))
if err != nil {
log.Print(err)
continue
}
db.Close()
if pwd == "" {
// New player
p2.authMech = AuthMechFirstSRP
binary.BigEndian.PutUint32(data[7:11], uint32(AuthMechFirstSRP))
} else {
// Existing player
p2.authMech = AuthMechSRP
binary.BigEndian.PutUint32(data[7:11], uint32(AuthMechSRP))
}
binary.BigEndian.PutUint16(data[11:13], uint16(len(p2.username)))
copy(data[13:], p2.username)
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
@ -547,54 +547,54 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
// Make sure the client is allowed to use AuthMechFirstSRP
if p2.authMech != AuthMechFirstSRP {
log.Print(p2.Addr().String() + " used unsupported AuthMechFirstSRP")
// Send ACCESS_DENIED
data := []byte{
uint8(0x00), uint8(0x0A),
uint8(0x01), uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
}
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
p2.SendDisco(0, true)
p2.Close()
return
}
// This is a new player, save verifier and salt
lenS := binary.BigEndian.Uint16(pkt.Data[2:4])
s := pkt.Data[4:4 + lenS]
lenV := binary.BigEndian.Uint16(pkt.Data[4 + lenS:6 + lenS])
v := pkt.Data[6 + lenS:6 + lenS + lenV]
s := pkt.Data[4 : 4+lenS]
lenV := binary.BigEndian.Uint16(pkt.Data[4+lenS : 6+lenS])
v := pkt.Data[6+lenS : 6+lenS+lenV]
pwd := encodeVerifierAndSalt(s, v)
db, err := initDB()
if err != nil {
log.Print(err)
continue
}
err = addAuthItem(db, string(p2.username), pwd)
if err != nil {
log.Print(err)
continue
}
err = addPrivItem(db, string(p2.username))
if err != nil {
log.Print(err)
continue
}
db.Close()
// Send AUTH_ACCEPT
data := []byte{
uint8(0x00), uint8(0x03),
@ -610,14 +610,14 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
// Sudo mode mechs
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x02),
}
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
// Connect to Minetest server
fin2 := make(chan struct{}) // close-only
Init(p2, p, ignMedia, fin2)
@ -626,68 +626,68 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
// Make sure the client is allowed to use AuthMechSRP
if p2.authMech != AuthMechSRP {
log.Print(p2.Addr().String() + " used unsupported AuthMechSRP")
// Send ACCESS_DENIED
data := []byte{
uint8(0x00), uint8(0x0A),
uint8(0x01), uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
}
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
p2.SendDisco(0, true)
p2.Close()
return
}
lenA := binary.BigEndian.Uint16(pkt.Data[2:4])
A := pkt.Data[4:4 + lenA]
A := pkt.Data[4 : 4+lenA]
db, err := initDB()
if err != nil {
log.Print(err)
continue
}
pwd, err := readAuthItem(db, string(p2.username))
if err != nil {
log.Print(err)
continue
}
db.Close()
s, v, err := decodeVerifierAndSalt(pwd)
if err != nil {
log.Print(err)
continue
}
B, _, K, err := srp.Handshake(A, v)
if err != nil {
log.Print(err)
continue
}
p2.srp_s = s
p2.srp_A = A
p2.srp_B = B
p2.srp_K = K
// Send SRP_BYTES_S_B
data := make([]byte, 6 + len(s) + len(B))
data := make([]byte, 6+len(s)+len(B))
data[0] = uint8(0x00)
data[1] = uint8(0x60)
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)
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)
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
@ -699,30 +699,30 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
// Make sure the client is allowed to use AuthMechSRP
if p2.authMech != AuthMechSRP {
log.Print(p2.Addr().String() + " used unsupported AuthMechSRP")
// Send ACCESS_DENIED
data := []byte{
uint8(0x00), uint8(0x0A),
uint8(0x01), uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
}
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
p2.SendDisco(0, true)
p2.Close()
return
}
lenM := binary.BigEndian.Uint16(pkt.Data[2:4])
M := pkt.Data[4:4 + lenM]
M := pkt.Data[4 : 4+lenM]
M2 := srp.CalculateM(p2.username, p2.srp_s, p2.srp_A, p2.srp_B, p2.srp_K)
if subtle.ConstantTimeCompare(M, M2) == 1 {
// Password is correct
// Send AUTH_ACCEPT
@ -740,34 +740,34 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
// Sudo mode mechs
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x02),
}
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
// Connect to Minetest server
fin2 := make(chan struct{}) // close-only
Init(p2, p, ignMedia, fin2)
} else {
} else {
// Client supplied wrong password
log.Print("User " + string(p2.username) + " at " + p2.Addr().String() + " supplied wrong password")
// Send ACCESS_DENIED
data := []byte{
uint8(0x00), uint8(0x0A),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
}
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
p2.SendDisco(0, true)
p2.Close()
return
@ -783,71 +783,71 @@ func Init(p, p2 *Peer, ignMedia bool, fin chan struct{}) {
// and redirects the client to srv2
func (p *Peer) Redirect(newsrv string) error {
defer processRedirectDone(p, newsrv)
straddr := GetConfKey("servers:" + newsrv + ":address")
if straddr == nil || fmt.Sprintf("%T", straddr) != "string" {
return ErrServerDoesNotExist
}
if p.Server().Addr().String() == straddr {
return ErrAlreadyConnected
}
srvaddr, err := net.ResolveUDPAddr("udp", straddr.(string))
if err != nil {
return err
}
conn, err := net.DialUDP("udp", nil, srvaddr)
if err != nil {
return err
}
srv := Connect(conn, conn.RemoteAddr())
if srv == nil {
return ErrServerUnreachable
}
// Remove active objects
len := 0
for _ = range aoIDs[p.ID()] {
len++
}
data := make([]byte, 6 + len * 2)
data := make([]byte, 6+len*2)
data[0] = uint8(0x00)
data[1] = uint8(0x31)
binary.BigEndian.PutUint16(data[2:4], uint16(len))
i := 4
for ao := range aoIDs[p.ID()] {
binary.BigEndian.PutUint16(data[i:2 + i], ao)
binary.BigEndian.PutUint16(data[i:2+i], ao)
i += 2
}
binary.BigEndian.PutUint16(data[i:2 + i], uint16(0))
binary.BigEndian.PutUint16(data[i:2+i], uint16(0))
ack, err := p.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
return err
}
<-ack
aoIDs[p.ID()] = make(map[uint16]bool)
p.initAoReceived = false
p.Server().StopForwarding()
fin := make(chan struct{}) // close-only
go Init(p, srv, true, fin)
<-fin
p.SetServer(srv)
go Proxy(p, srv)
go Proxy(srv, p)
log.Print(p.Addr().String() + " redirected to " + newsrv)
return nil
}
@ -860,17 +860,17 @@ func encodeVerifierAndSalt(s, v []byte) string {
func decodeVerifierAndSalt(src string) ([]byte, []byte, error) {
sString := strings.Split(src, "#")[0]
vString := strings.Split(src, "#")[1]
s, err := base64.StdEncoding.DecodeString(sString)
if err != nil {
return nil, nil, err
}
v, err := base64.StdEncoding.DecodeString(vString)
if err != nil {
return nil, nil, err
}
return s, v, nil
}
@ -885,7 +885,7 @@ func initDB() (*sql.DB, error) {
if db == nil {
panic("DB is nil")
}
sql_table := `CREATE TABLE IF NOT EXISTS auth (
name VARCHAR(32) NOT NULL,
password VARCHAR(512) NOT NULL
@ -895,12 +895,12 @@ func initDB() (*sql.DB, error) {
privileges VARCHAR(1024)
);
`
_, err = db.Exec(sql_table)
if err != nil {
return nil, err
}
return db, nil
}
@ -914,60 +914,60 @@ func addAuthItem(db *sql.DB, name, password string) error {
?
);
`
stmt, err := db.Prepare(sql_addAuthItem)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(name, password)
if err != nil {
return err
}
return nil
}
// modAuthItem updates an auth DB entry
func modAuthItem(db *sql.DB, name, password string) error {
sql_modAuthItem := `UPDATE auth SET password = ? WHERE name = ?;`
stmt, err := db.Prepare(sql_modAuthItem)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(password, name)
if err != nil {
return err
}
return nil
}
// readAuthItem selects and reads an auth DB entry
func readAuthItem(db *sql.DB, name string) (string, error) {
sql_readAuthItem := `SELECT password FROM auth WHERE name = ?;`
stmt, err := db.Prepare(sql_readAuthItem)
if err != nil {
return "", err
}
defer stmt.Close()
rows, err := stmt.Query(name)
if err != nil {
return "", err
}
var r string
for rows.Next() {
err = rows.Scan(&r)
}
return r, nil
}

View File

@ -17,14 +17,14 @@ func LoadPlugins() error {
if err != nil {
return err
}
for _, file := range files {
if file.IsDir() {
subfiles, err := ioutil.ReadDir("plugins/" + file.Name())
if err != nil {
return err
}
for _, subfile := range subfiles {
if subfile.Name() == "init.lua" {
log.Print("Loading plugin " + file.Name())
@ -36,6 +36,6 @@ func LoadPlugins() error {
}
}
}
return nil
}

View File

@ -1,9 +1,9 @@
package multiserver
import (
"strings"
"database/sql"
_ "github.com/mattn/go-sqlite3"
"strings"
)
// encodePrivs encodes priv map into DB-ready string
@ -14,33 +14,33 @@ func encodePrivs(privs map[string]bool) string {
lenP++
}
}
ps := make([]string, lenP)
i := 0
for priv := range privs {
if privs[priv] {
ps[i] = priv
i++
}
}
r := strings.Join(ps, "|")
return r
}
// decodePrivs decodes DB-ready string into priv map
func decodePrivs(s string) map[string]bool {
ps := strings.Split(s, "|")
r := make(map[string]bool)
for i := range ps {
r[ps[i]] = true
}
return r
}
@ -54,59 +54,59 @@ func addPrivItem(db *sql.DB, name string) error {
""
);
`
stmt, err := db.Prepare(sql_addPrivItem)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(name)
if err != nil {
return err
}
return nil
}
// modPrivItem updates a priv DB entry
func modPrivItem(db *sql.DB, name, privs string) error {
sql_modPrivItem := `UPDATE privileges SET privileges = ? WHERE name = ?;`
stmt, err := db.Prepare(sql_modPrivItem)
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(privs, name)
if err != nil {
return err
}
return nil
}
// readPrivItem selects and reads a priv DB entry
func readPrivItem(db *sql.DB, name string) (string, error) {
sql_readPrivItem := `SELECT privileges FROM privileges WHERE name = ?;`
stmt, err := db.Prepare(sql_readPrivItem)
if err != nil {
return "", err
}
defer stmt.Close()
rows, err := stmt.Query(name)
if err != nil {
return "", err
}
var r string
for rows.Next() {
err = rows.Scan(&r)
}
return r, nil
}

View File

@ -1,11 +1,11 @@
package multiserver
import (
"encoding/binary"
"encoding/hex"
"errors"
"fmt"
"io"
"encoding/binary"
"errors"
)
// A PktError is an error that occured while processing a packet
@ -16,7 +16,7 @@ type PktError struct {
}
func (e PktError) Error() string {
return "error processing " + e.Type + " pkt: " +
return "error processing " + e.Type + " pkt: " +
hex.EncodeToString(e.Data) + ": " +
e.Err.Error()
}
@ -29,7 +29,7 @@ func (p *Peer) processNetPkts(pkts <-chan netPkt) {
p.errs <- PktError{"net", pkt.Data, err}
}
}
close(p.pkts)
}
@ -45,37 +45,37 @@ func (p *Peer) processNetPkt(pkt netPkt) (err error) {
if pkt.SrcAddr.String() != p.Addr().String() {
return fmt.Errorf("got pkt from wrong addr: %s", p.Addr().String())
}
if len(pkt.Data) < MtHdrSize {
return io.ErrUnexpectedEOF
}
if id := binary.BigEndian.Uint32(pkt.Data[0:4]); id != protoID {
return fmt.Errorf("unsupported protocol id: 0x%08x", id)
}
// src PeerID at pkt.Data[4:6]
chno := pkt.Data[6]
if chno >= ChannelCount {
return fmt.Errorf("invalid channel number: %d: >= ChannelCount", chno)
}
p.mu.RLock()
if p.timeout != nil {
p.timeout.Reset(ConnTimeout)
}
p.mu.RUnlock()
rpkt := rawPkt{
Data: pkt.Data[MtHdrSize:],
ChNo: chno,
Unrel: true,
Data: pkt.Data[MtHdrSize:],
ChNo: chno,
Unrel: true,
}
if err := p.processRawPkt(rpkt); err != nil {
p.errs <- PktError{"raw", rpkt.Data, err}
}
return nil
}
@ -85,167 +85,167 @@ func (p *Peer) processRawPkt(pkt rawPkt) (err error) {
err = fmt.Errorf(format, append(a, err)...)
}
}
c := &p.chans[pkt.ChNo]
if len(pkt.Data) < 1 {
return fmt.Errorf("can't read pkt type: %w", io.ErrUnexpectedEOF)
}
switch t := rawType(pkt.Data[0]); t {
case rawTypeCtl:
defer errWrap("ctl: %w")
if len(pkt.Data) < 1 + 1 {
if len(pkt.Data) < 1+1 {
return fmt.Errorf("can't read type: %w", io.ErrUnexpectedEOF)
}
switch ct := ctlType(pkt.Data[1]); ct {
case ctlAck:
defer errWrap("ack: %w")
if len(pkt.Data) < 1 + 1 + 2 {
if len(pkt.Data) < 1+1+2 {
return io.ErrUnexpectedEOF
}
sn := seqnum(binary.BigEndian.Uint16(pkt.Data[2:4]))
if ack, ok := c.ackchans.LoadAndDelete(sn); ok {
close(ack.(chan struct{}))
}
if len(pkt.Data) > 1 + 2 + 2 {
return TrailingDataError(pkt.Data[1 + 1 + 2:])
if len(pkt.Data) > 1+2+2 {
return TrailingDataError(pkt.Data[1+1+2:])
}
case ctlSetPeerID:
defer errWrap("set peer id: %w")
if len(pkt.Data) < 1 + 1 + 2 {
if len(pkt.Data) < 1+1+2 {
return io.ErrUnexpectedEOF
}
// Ensure no concurrent senders while peer id changes
p.mu.Lock()
if p.idOfPeer != PeerIDNil {
return errors.New("peer id already set")
}
p.idOfPeer = PeerID(binary.BigEndian.Uint16(pkt.Data[2:4]))
p.mu.Unlock()
if len(pkt.Data) > 1 + 1 + 2 {
return TrailingDataError(pkt.Data[1 + 1 + 2:])
if len(pkt.Data) > 1+1+2 {
return TrailingDataError(pkt.Data[1+1+2:])
}
case ctlPing:
defer errWrap("ping: %w")
if len(pkt.Data) > 1 + 1 {
return TrailingDataError(pkt.Data[1 + 1:])
if len(pkt.Data) > 1+1 {
return TrailingDataError(pkt.Data[1+1:])
}
case ctlDisco:
defer errWrap("disco: %w")
if err := p.Close(); err != nil {
return fmt.Errorf("can't close: %w", err)
}
if len(pkt.Data) > 1 + 1 {
return TrailingDataError(pkt.Data[1 + 1:])
if len(pkt.Data) > 1+1 {
return TrailingDataError(pkt.Data[1+1:])
}
default:
return fmt.Errorf("unsupported ctl type: %d", ct)
}
case rawTypeOrig:
p.pkts <- Pkt{
Data: pkt.Data[1:],
ChNo: pkt.ChNo,
Unrel: pkt.Unrel,
Data: pkt.Data[1:],
ChNo: pkt.ChNo,
Unrel: pkt.Unrel,
}
case rawTypeSplit:
defer errWrap("split: %w")
if len(pkt.Data) < 1 + 2 + 2 + 2 {
if len(pkt.Data) < 1+2+2+2 {
return io.ErrUnexpectedEOF
}
sn := seqnum(binary.BigEndian.Uint16(pkt.Data[1:3]))
count := binary.BigEndian.Uint16(pkt.Data[3:5])
i := binary.BigEndian.Uint16(pkt.Data[5:7])
if i >= count {
return nil
}
splitpkts := p.chans[pkt.ChNo].insplit
// Delete old incomplete split packets
// so new ones don't get corrupted
delete(splitpkts, sn - 0x8000)
delete(splitpkts, sn-0x8000)
if splitpkts[sn] == nil {
splitpkts[sn] = make([][]byte, count)
}
chunks := splitpkts[sn]
if int(count) != len(chunks) {
return fmt.Errorf("chunk count changed on seqnum: %d", sn)
}
chunks[i] = pkt.Data[7:]
for _, chunk := range chunks {
if chunk == nil {
return nil
}
}
var data []byte
for _, chunk := range chunks {
data = append(data, chunk...)
}
p.pkts <- Pkt{
Data: data,
ChNo: pkt.ChNo,
Unrel: pkt.Unrel,
Data: data,
ChNo: pkt.ChNo,
Unrel: pkt.Unrel,
}
delete(splitpkts, sn)
case rawTypeRel:
defer errWrap("rel: %w")
if len(pkt.Data) < 1 + 2 {
if len(pkt.Data) < 1+2 {
return io.ErrUnexpectedEOF
}
sn := seqnum(binary.BigEndian.Uint16(pkt.Data[1:3]))
ackdata := make([]byte, 1 + 1 + 2)
ackdata := make([]byte, 1+1+2)
ackdata[0] = uint8(rawTypeCtl)
ackdata[1] = uint8(ctlAck)
binary.BigEndian.PutUint16(ackdata[2:4], uint16(sn))
ack := rawPkt{
Data: ackdata,
ChNo: pkt.ChNo,
Unrel: true,
Data: ackdata,
ChNo: pkt.ChNo,
Unrel: true,
}
if _, err := p.sendRaw(ack); err != nil {
return fmt.Errorf("can't ack %d: %w", sn, err)
}
if sn - c.inrelsn >= 0x8000 {
if sn-c.inrelsn >= 0x8000 {
return nil // Already received
}
c.inrel[sn] = pkt.Data[3:]
for ; c.inrel[c.inrelsn] != nil; c.inrelsn++ {
data := c.inrel[c.inrelsn]
delete(c.inrel, c.inrelsn)
rpkt := rawPkt{
Data: data,
ChNo: pkt.ChNo,
Unrel: false,
Data: data,
ChNo: pkt.ChNo,
Unrel: false,
}
if err := p.processRawPkt(rpkt); err != nil {
p.errs <- PktError{"rel", rpkt.Data, err}
@ -254,6 +254,6 @@ func (p *Peer) processRawPkt(pkt rawPkt) (err error) {
default:
return fmt.Errorf("unsupported pkt type: %d", t)
}
return nil
}

View File

@ -10,10 +10,10 @@ type PeerID uint16
const (
// Used by clients before the server sets their ID
PeerIDNil PeerID = iota
// The server always has this ID
PeerIDSrv
// Lowest ID the server can assign to a client
PeerIDCltMin
)
@ -22,9 +22,9 @@ const (
const ChannelCount = 3
type rawPkt struct {
Data []byte
ChNo uint8
Unrel bool
Data []byte
ChNo uint8
Unrel bool
}
type rawType uint8
@ -46,9 +46,9 @@ const (
)
type Pkt struct {
Data []byte
ChNo uint8
Unrel bool
Data []byte
ChNo uint8
Unrel bool
}
// seqnums are sequence numbers used to maintain reliable packet order

View File

@ -17,19 +17,19 @@ func Proxy(src, dst *Peer) {
msg += " (timed out)"
}
log.Print(msg)
if !src.IsSrv() {
connectedPeers--
processLeave(src.ID())
}
break
}
log.Print(err)
continue
}
// Process
// Chat message
if pkt.Data[0] == uint8(0x00) && pkt.Data[1] == uint8(0x32) && !src.IsSrv() {
@ -41,13 +41,13 @@ func Proxy(src, dst *Peer) {
if pkt.Data[0] == uint8(0x00) && pkt.Data[1] == uint8(0x31) && src.IsSrv() {
processAORmAdd(dst, pkt.Data)
}
// Forward
if _, err := dst.Send(pkt); err != nil {
log.Print(err)
}
}
dst.SendDisco(0, true)
dst.Close()
}

140
send.go
View File

@ -1,36 +1,36 @@
package multiserver
import (
"math"
"errors"
"sync"
"encoding/binary"
"time"
"errors"
"fmt"
"math"
"net"
"sync"
"time"
)
const (
// protoID + src PeerID + channel number
MtHdrSize = 4 + 2 + 1
// rawTypeOrig
OrigHdrSize = 1
// rawTypeSplit + seqnum + chunk count + chunk number
SplitHdrSize = 1 + 2 + 2 + 2
// rawTypeRel + seqnum
RelHdrSize = 1 + 2
)
const (
MaxNetPktSize = 512
MaxUnrelRawPktSize = MaxNetPktSize - MtHdrSize
MaxRelRawPktSize = MaxUnrelRawPktSize - RelHdrSize
MaxRelPktSize = (MaxRelRawPktSize - SplitHdrSize) * math.MaxUint16
MaxUnrelRawPktSize = MaxNetPktSize - MtHdrSize
MaxRelRawPktSize = MaxUnrelRawPktSize - RelHdrSize
MaxRelPktSize = (MaxRelRawPktSize - SplitHdrSize) * math.MaxUint16
MaxUnrelPktSize = (MaxUnrelRawPktSize - SplitHdrSize) * math.MaxUint16
)
@ -44,41 +44,41 @@ func (p *Peer) Send(pkt Pkt) (ack <-chan struct{}, err error) {
if pkt.ChNo >= ChannelCount {
return nil, ErrChNoTooBig
}
hdrsize := MtHdrSize
if !pkt.Unrel {
hdrsize += RelHdrSize
}
if hdrsize + OrigHdrSize + len(pkt.Data) > MaxNetPktSize {
if hdrsize+OrigHdrSize+len(pkt.Data) > MaxNetPktSize {
c := &p.chans[pkt.ChNo]
c.outsplitmu.Lock()
sn := c.outsplitsn
c.outsplitsn++
c.outsplitmu.Unlock()
chunks := split(pkt.Data, MaxNetPktSize - (hdrsize + SplitHdrSize))
chunks := split(pkt.Data, MaxNetPktSize-(hdrsize+SplitHdrSize))
if len(chunks) > math.MaxUint16 {
return nil, ErrPktTooBig
}
var wg sync.WaitGroup
for i, chunk := range chunks {
data := make([]byte, SplitHdrSize + len(chunk))
data := make([]byte, SplitHdrSize+len(chunk))
data[0] = uint8(rawTypeSplit)
binary.BigEndian.PutUint16(data[1:3], uint16(sn))
binary.BigEndian.PutUint16(data[3:5], uint16(len(chunks)))
binary.BigEndian.PutUint16(data[5:7], uint16(i))
copy(data[SplitHdrSize:], chunk)
wg.Add(1)
ack, err := p.sendRaw(rawPkt{
Data: data,
ChNo: pkt.ChNo,
Unrel: pkt.Unrel,
Data: data,
ChNo: pkt.ChNo,
Unrel: pkt.Unrel,
})
if err != nil {
return nil, err
@ -93,25 +93,25 @@ func (p *Peer) Send(pkt Pkt) (ack <-chan struct{}, err error) {
}()
}
}
if pkt.Unrel {
return nil, nil
} else {
ack := make(chan struct{})
go func() {
wg.Wait()
close(ack)
}()
return ack, nil
}
}
return p.sendRaw(rawPkt{
Data: append([]byte{uint8(rawTypeOrig)}, pkt.Data...),
ChNo: pkt.ChNo,
Unrel: pkt.Unrel,
Data: append([]byte{uint8(rawTypeOrig)}, pkt.Data...),
ChNo: pkt.ChNo,
Unrel: pkt.Unrel,
})
}
@ -120,30 +120,30 @@ func (p *Peer) sendRaw(pkt rawPkt) (ack <-chan struct{}, err error) {
if pkt.ChNo >= ChannelCount {
return nil, ErrChNoTooBig
}
p.mu.RLock()
defer p.mu.RUnlock()
select {
case <-p.Disco():
return nil, ErrClosed
default:
}
if !pkt.Unrel {
return p.sendRel(pkt)
}
data := make([]byte, MtHdrSize + len(pkt.Data))
data := make([]byte, MtHdrSize+len(pkt.Data))
binary.BigEndian.PutUint32(data[0:4], protoID)
binary.BigEndian.PutUint16(data[4:6], uint16(p.idOfPeer))
data[6] = pkt.ChNo
copy(data[MtHdrSize:], pkt.Data)
if len(data) > MaxNetPktSize {
return nil, ErrPktTooBig
}
_, err = p.Conn().WriteTo(data, p.Addr())
if errors.Is(err, net.ErrWriteToConnected) {
conn, ok := p.Conn().(net.Conn)
@ -155,9 +155,9 @@ func (p *Peer) sendRaw(pkt rawPkt) (ack <-chan struct{}, err error) {
if err != nil {
return nil, err
}
p.ping.Reset(PingTimeout)
return nil, nil
}
@ -166,44 +166,44 @@ func (p *Peer) sendRel(pkt rawPkt) (ack <-chan struct{}, err error) {
if pkt.Unrel {
panic("sendRel: pkt.Unrel is true")
}
c := &p.chans[pkt.ChNo]
c.outrelmu.Lock()
defer c.outrelmu.Unlock()
sn := c.outrelsn
for ; sn - c.outrelwin >= 0x8000; c.outrelwin++ {
for ; sn-c.outrelwin >= 0x8000; c.outrelwin++ {
if ack, ok := c.ackchans.Load(c.outrelwin); ok {
<-ack.(chan struct{})
}
}
c.outrelsn++
rwack := make(chan struct{}) // close-only
c.ackchans.Store(sn, rwack)
ack = rwack
reldata := make([]byte, RelHdrSize + len(pkt.Data))
reldata := make([]byte, RelHdrSize+len(pkt.Data))
reldata[0] = uint8(rawTypeRel)
binary.BigEndian.PutUint16(reldata[1:3], uint16(sn))
copy(reldata[RelHdrSize:], pkt.Data)
relpkt := rawPkt{
Data: reldata,
ChNo: pkt.ChNo,
Unrel: true,
Data: reldata,
ChNo: pkt.ChNo,
Unrel: true,
}
if _, err := p.sendRaw(relpkt); err != nil {
c.ackchans.Delete(sn)
return nil, err
}
go func() {
resend := time.NewTicker(500 * time.Millisecond)
defer resend.Stop()
for {
select {
case <-resend.C:
@ -217,23 +217,23 @@ func (p *Peer) sendRel(pkt rawPkt) (ack <-chan struct{}, err error) {
}
}
}()
return ack, nil
}
// sendAck sends an ack for sn to the Peer
func (p *Peer) sendAck(chno uint8, unrel bool, sn seqnum) (ack <-chan struct{}, err error) {
data := make([]byte, 1 + 1 + 2)
data := make([]byte, 1+1+2)
data[0] = uint8(rawTypeCtl)
data[1] = uint8(ctlAck)
binary.BigEndian.PutUint16(data[2:4], uint16(sn))
pkt := rawPkt{
Data: data,
ChNo: chno,
Unrel: unrel,
Data: data,
ChNo: chno,
Unrel: unrel,
}
return p.sendRaw(pkt)
}
@ -242,23 +242,23 @@ func (p *Peer) sendAck(chno uint8, unrel bool, sn seqnum) (ack <-chan struct{},
// The ack channel is nil if unrel is true
func (p *Peer) SendDisco(chno uint8, unrel bool) (ack <-chan struct{}, err error) {
return p.sendRaw(rawPkt{
Data: []byte{uint8(rawTypeCtl), uint8(ctlDisco)},
ChNo: chno,
Unrel: unrel,
Data: []byte{uint8(rawTypeCtl), uint8(ctlDisco)},
ChNo: chno,
Unrel: unrel,
})
}
func split(data []byte, chunksize int) [][]byte {
chunks := make([][]byte, 0, (len(data) + chunksize - 1) / chunksize)
chunks := make([][]byte, 0, (len(data)+chunksize-1)/chunksize)
for i := 0; i < len(data); i += chunksize {
end := i + chunksize
if end > len(data) {
end = len(data)
}
chunks = append(chunks, data[i:end])
}
return chunks
}