Split peer methods across more files + Automatic config and auth file creation

master
HimbeerserverDE 2021-01-09 22:01:36 +01:00
parent e58324909e
commit be93ff59af
9 changed files with 744 additions and 704 deletions

131
auth.go Normal file
View File

@ -0,0 +1,131 @@
package multiserver
import (
"database/sql"
"encoding/base64"
"os"
"strings"
_ "github.com/mattn/go-sqlite3"
)
// encodeVerifierAndSalt encodes SRP verifier and salt into DB-ready string
func encodeVerifierAndSalt(s, v []byte) string {
return base64.StdEncoding.EncodeToString(s) + "#" + base64.StdEncoding.EncodeToString(v)
}
// decodeVerifierAndSalt decodes DB-ready string into SRP verifier and salt
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
}
// initAuthDB opens auth.sqlite and creates the required tables
// if they don't exist
// It returns said database
func initAuthDB() (*sql.DB, error) {
os.Mkdir("storage", 0775)
db, err := sql.Open("sqlite3", "storage/auth.sqlite")
if err != nil {
return nil, err
}
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
);
CREATE TABLE IF NOT EXISTS privileges (
name VARCHAR(32) NOT NULL,
privileges VARCHAR(1024)
);
`
_, err = db.Exec(sql_table)
if err != nil {
return nil, err
}
return db, nil
}
// addAuthItem inserts an auth DB entry
func addAuthItem(db *sql.DB, name, password string) error {
sql_addAuthItem := `INSERT INTO auth (
name,
password
) VALUES (
?,
?
);
`
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

@ -2,15 +2,29 @@ package multiserver
import (
"io/ioutil"
"os"
"strings"
"gopkg.in/yaml.v2"
)
var Config map[interface{}]interface{}
var defaultConfig []byte = []byte(`host: "0.0.0.0:33000"
player_limit: -1
servers:
lobby:
address: "127.0.0.1:30000"
`)
// LoadConfig loads the configuration file
func LoadConfig() error {
os.Mkdir("config", 0775)
_, err := os.Stat("config/multiserver.yml")
if os.IsNotExist(err) {
ioutil.WriteFile("config/multiserver.yml", defaultConfig, 0775)
}
data, err := ioutil.ReadFile("config/multiserver.yml")
if err != nil {
return err

508
init.go Normal file
View File

@ -0,0 +1,508 @@
package multiserver
import (
"crypto/subtle"
"encoding/binary"
"errors"
"log"
"strings"
"time"
"github.com/HimbeerserverDE/srp"
)
var ErrAuthFailed = errors.New("authentication failure")
// Init authenticates to the server srv
// and finishes the initialisation process if ignMedia is true
// 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[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(0x0025))
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 {
if err == ErrClosed {
msg := p2.Addr().String() + " disconnected"
if p2.TimedOut() {
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 {
// 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[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)
ack, err := p2.Send(Pkt{Data: data, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
} else {
// Compute and send s and v
s, v, err := srp.NewClient([]byte(strings.ToLower(string(p.username))), passPhrase)
if err != nil {
log.Print(err)
continue
}
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)
ack, err := p2.Send(Pkt{Data: data, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
}
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:]
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[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)
continue
}
<-ack
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
case 0x03:
// Auth succeeded
ack, err := p2.Send(Pkt{Data: []byte{uint8(0), uint8(0x11), uint8(0), uint8(0)}, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
if !ignMedia {
return
}
case 0x2A:
// Definitions sent (by server)
if !ignMedia {
continue
}
v := []byte("5.4.0-dev-dd5a732fa")
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
}
}
} else {
for {
pkt, err := p2.Recv()
if err != nil {
if err == ErrClosed {
msg := p2.Addr().String() + " disconnected"
if p2.TimedOut() {
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[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 := initAuthDB()
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)
continue
}
<-ack
case 0x50:
// Process data
// 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]
pwd := encodeVerifierAndSalt(s, v)
db, err := initAuthDB()
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),
// Position stuff
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
// Map seed
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
// Send interval
uint8(0x3D), uint8(0xB8), uint8(0x51), uint8(0xEC),
// 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)
case 0x51:
// Process data
// 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]
db, err := initAuthDB()
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[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)
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
case 0x52:
// Process data
// 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]
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
data := []byte{
uint8(0x00), uint8(0x03),
// Position stuff
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
// Map seed
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
// Send interval
uint8(0x3D), uint8(0xB8), uint8(0x51), uint8(0xEC),
// 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 {
// 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
}
case 0x11:
return
}
}
}
}

View File

@ -53,7 +53,7 @@ func processChatMessage(peerid PeerID, msg []byte) bool {
for i := range chatCommands {
if chatCommands[i].name == params[0] {
// Priv check
db, err := initDB()
db, err := initAuthDB()
if err != nil {
log.Print(err)
return true

View File

@ -11,7 +11,7 @@ func getPlayerPrivs(L *lua.LState) int {
r := L.NewTable()
db, err := initDB()
db, err := initAuthDB()
if err != nil {
log.Print(err)
@ -58,7 +58,7 @@ func setPlayerPrivs(L *lua.LState) int {
ps := encodePrivs(newpmap)
db, err := initDB()
db, err := initAuthDB()
if err != nil {
log.Print(err)
@ -79,7 +79,7 @@ func checkPlayerPrivs(L *lua.LState) int {
name := L.ToString(1)
reqprivs := L.ToTable(2)
db, err := initDB()
db, err := initAuthDB()
if err != nil {
log.Print(err)

2
multiserver/config/multiserver.yml Normal file → Executable file
View File

@ -3,3 +3,5 @@ player_limit: -1
servers:
lobby:
address: "127.0.0.1:30000"
map2:
address: "127.0.0.1:30001"

View File

@ -5,7 +5,6 @@ import (
"log"
"net"
"time"
"github.com/HimbeerserverDE/multiserver"
)

698
peer.go
View File

@ -1,25 +1,14 @@
package multiserver
import (
"crypto/subtle"
"database/sql"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"log"
"net"
"strings"
"sync"
"time"
"github.com/HimbeerserverDE/srp"
_ "github.com/mattn/go-sqlite3"
)
var ErrAuthFailed = errors.New("authentication failure")
var ErrServerDoesNotExist = errors.New("server doesn't exist")
var ErrAlreadyConnected = errors.New("already connected to server")
var ErrServerUnreachable = errors.New("server is unreachable")
var passPhrase []byte = []byte("jK7BPRoxM9ffwh7Z")
@ -284,693 +273,6 @@ func Connect(conn net.PacketConn, addr net.Addr) *Peer {
return srv
}
// Init authenticates to the server srv
// and finishes the initialisation process if ignMedia is true
// 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[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(0x0025))
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 {
if err == ErrClosed {
msg := p2.Addr().String() + " disconnected"
if p2.TimedOut() {
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 {
// 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[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)
ack, err := p2.Send(Pkt{Data: data, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
} else {
// Compute and send s and v
s, v, err := srp.NewClient([]byte(strings.ToLower(string(p.username))), passPhrase)
if err != nil {
log.Print(err)
continue
}
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)
ack, err := p2.Send(Pkt{Data: data, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
}
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:]
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[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)
continue
}
<-ack
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
case 0x03:
// Auth succeeded
ack, err := p2.Send(Pkt{Data: []byte{uint8(0), uint8(0x11), uint8(0), uint8(0)}, ChNo: 1, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
if !ignMedia {
return
}
case 0x2A:
// Definitions sent (by server)
if !ignMedia {
continue
}
v := []byte("5.4.0-dev-dd5a732fa")
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
}
}
} else {
for {
pkt, err := p2.Recv()
if err != nil {
if err == ErrClosed {
msg := p2.Addr().String() + " disconnected"
if p2.TimedOut() {
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[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)
continue
}
<-ack
case 0x50:
// Process data
// 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]
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),
// Position stuff
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
// Map seed
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
// Send interval
uint8(0x3D), uint8(0xB8), uint8(0x51), uint8(0xEC),
// 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)
case 0x51:
// Process data
// 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]
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[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)
ack, err := p2.Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
continue
}
<-ack
case 0x52:
// Process data
// 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]
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
data := []byte{
uint8(0x00), uint8(0x03),
// Position stuff
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
// Map seed
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
uint8(0x00), uint8(0x00), uint8(0x00), uint8(0x00),
// Send interval
uint8(0x3D), uint8(0xB8), uint8(0x51), uint8(0xEC),
// 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 {
// 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
}
case 0x11:
return
}
}
}
}
// Redirect closes the connection to srv1
// 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[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)
i += 2
}
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
}
// encodeVerifierAndSalt encodes SRP verifier and salt into DB-ready string
func encodeVerifierAndSalt(s, v []byte) string {
return base64.StdEncoding.EncodeToString(s) + "#" + base64.StdEncoding.EncodeToString(v)
}
// decodeVerifierAndSalt decodes DB-ready string into SRP verifier and salt
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
}
// initDB opens auth.sqlite and creates the required tables
// if they don't exist
// It returns said database
func initDB() (*sql.DB, error) {
db, err := sql.Open("sqlite3", "storage/auth.sqlite")
if err != nil {
return nil, err
}
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
);
CREATE TABLE IF NOT EXISTS privileges (
name VARCHAR(32) NOT NULL,
privileges VARCHAR(1024)
);
`
_, err = db.Exec(sql_table)
if err != nil {
return nil, err
}
return db, nil
}
// addAuthItem inserts an auth DB entry
func addAuthItem(db *sql.DB, name, password string) error {
sql_addAuthItem := `INSERT INTO auth (
name,
password
) VALUES (
?,
?
);
`
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
}
func GetPeerCount() int {
return connectedPeers
}

84
redirect.go Normal file
View File

@ -0,0 +1,84 @@
package multiserver
import (
"encoding/binary"
"errors"
"fmt"
"log"
"net"
)
var ErrServerDoesNotExist = errors.New("server doesn't exist")
var ErrAlreadyConnected = errors.New("already connected to server")
// Redirect closes the connection to srv1
// 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[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)
i += 2
}
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
}