go fmt
parent
8391275b5e
commit
9b12ce30c4
|
@ -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
|
||||
}
|
||||
|
|
14
config.go
14
config.go
|
@ -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
16
end.go
|
@ -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 {
|
||||
|
|
124
l_chatmessage.go
124
l_chatmessage.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
4
l_end.go
4
l_end.go
|
@ -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
|
||||
}
|
||||
|
|
4
l_log.go
4
l_log.go
|
@ -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
|
||||
}
|
||||
|
|
66
l_player.go
66
l_player.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
76
listen.go
76
listen.go
|
@ -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
4
lua.go
|
@ -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")
|
||||
|
|
|
@ -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
12
net.go
|
@ -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
412
peer.go
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
40
privs.go
40
privs.go
|
@ -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
|
||||
}
|
||||
|
|
154
process.go
154
process.go
|
@ -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
|
||||
}
|
||||
|
|
16
proto.go
16
proto.go
|
@ -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
|
||||
|
|
12
proxy.go
12
proxy.go
|
@ -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
140
send.go
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue