Document exported code (#49)
parent
04d33fd100
commit
7e72caa092
6
auth.go
6
auth.go
|
@ -5,7 +5,7 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var authIface AuthBackend
|
||||
var authIface authBackend
|
||||
var ErrAuthBackendExists = errors.New("auth backend already set")
|
||||
|
||||
type user struct {
|
||||
|
@ -15,7 +15,7 @@ type user struct {
|
|||
timestamp time.Time
|
||||
}
|
||||
|
||||
type AuthBackend interface {
|
||||
type authBackend interface {
|
||||
Exists(name string) bool
|
||||
Passwd(name string) (salt, verifier []byte, err error)
|
||||
SetPasswd(name string, salt, verifier []byte) error
|
||||
|
@ -24,7 +24,7 @@ type AuthBackend interface {
|
|||
Export() ([]user, error)
|
||||
}
|
||||
|
||||
func SetAuthBackend(ab AuthBackend) error {
|
||||
func setAuthBackend(ab authBackend) error {
|
||||
if authIface != nil {
|
||||
return ErrAuthBackendExists
|
||||
}
|
||||
|
|
|
@ -9,11 +9,12 @@ import (
|
|||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
type AuthSQLite3 struct {
|
||||
type authSQLite3 struct {
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func (a AuthSQLite3) Exists(name string) bool {
|
||||
// Exists reports whether a user is registered.
|
||||
func (a authSQLite3) Exists(name string) bool {
|
||||
if err := a.init(); err != nil {
|
||||
return false
|
||||
}
|
||||
|
@ -24,7 +25,8 @@ func (a AuthSQLite3) Exists(name string) bool {
|
|||
return err == nil
|
||||
}
|
||||
|
||||
func (a AuthSQLite3) Passwd(name string) (salt, verifier []byte, err error) {
|
||||
// Passwd returns the SRP salt and verifier of a user or an error.
|
||||
func (a authSQLite3) Passwd(name string) (salt, verifier []byte, err error) {
|
||||
if err = a.init(); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -35,7 +37,9 @@ func (a AuthSQLite3) Passwd(name string) (salt, verifier []byte, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (a AuthSQLite3) SetPasswd(name string, salt, verifier []byte) error {
|
||||
// SetPasswd creates a password entry if necessary
|
||||
// and sets the password of a user.
|
||||
func (a authSQLite3) SetPasswd(name string, salt, verifier []byte) error {
|
||||
if err := a.init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -50,7 +54,9 @@ func (a AuthSQLite3) SetPasswd(name string, salt, verifier []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a AuthSQLite3) Timestamp(name string) (time.Time, error) {
|
||||
// Timestamp returns the last time an authentication entry was accessed
|
||||
// or an error.
|
||||
func (a authSQLite3) Timestamp(name string) (time.Time, error) {
|
||||
if err := a.init(); err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
@ -65,7 +71,9 @@ func (a AuthSQLite3) Timestamp(name string) (time.Time, error) {
|
|||
return time.Parse("2006-01-02 15:04:05", tstr)
|
||||
}
|
||||
|
||||
func (a AuthSQLite3) Import(in []user) {
|
||||
// Import clears the database and and refills it with the passed
|
||||
// users.
|
||||
func (a authSQLite3) Import(in []user) {
|
||||
if err := a.init(); err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -78,7 +86,9 @@ func (a AuthSQLite3) Import(in []user) {
|
|||
}
|
||||
}
|
||||
|
||||
func (a AuthSQLite3) Export() ([]user, error) {
|
||||
// Export returns data that can be processed by Import
|
||||
// or an error.
|
||||
func (a authSQLite3) Export() ([]user, error) {
|
||||
if err := a.init(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -119,11 +129,11 @@ func (a AuthSQLite3) Export() ([]user, error) {
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (a AuthSQLite3) updateTimestamp(name string) {
|
||||
func (a authSQLite3) updateTimestamp(name string) {
|
||||
a.db.Exec(`UPDATE user SET timestamp = datetime("now") WHERE name = ?;`, name)
|
||||
}
|
||||
|
||||
func (a *AuthSQLite3) init() error {
|
||||
func (a *authSQLite3) init() error {
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -148,6 +158,6 @@ func (a *AuthSQLite3) init() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a AuthSQLite3) close() error {
|
||||
func (a authSQLite3) close() error {
|
||||
return a.db.Close()
|
||||
}
|
||||
|
|
2
chat.go
2
chat.go
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/anon55555/mt"
|
||||
)
|
||||
|
||||
// SendChatMsg sends a chat message to the ClientConn.
|
||||
func (cc *ClientConn) SendChatMsg(msg ...string) {
|
||||
cc.SendCmd(&mt.ToCltChatMsg{
|
||||
Type: mt.SysMsg,
|
||||
|
@ -16,6 +17,7 @@ func (cc *ClientConn) SendChatMsg(msg ...string) {
|
|||
})
|
||||
}
|
||||
|
||||
// Colorize returns the minetest-colorized version of the input.
|
||||
func Colorize(text, color string) string {
|
||||
return string(0x1b) + "(c@" + color + ")" + text + string(0x1b) + "(c@#FFF)"
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ const (
|
|||
csSudo
|
||||
)
|
||||
|
||||
// A ClientConn is a connection to a minetest client.
|
||||
type ClientConn struct {
|
||||
mt.Peer
|
||||
srv *ServerConn
|
||||
|
@ -60,6 +61,7 @@ type ClientConn struct {
|
|||
modChs map[string]struct{}
|
||||
}
|
||||
|
||||
// Name returns the player name of the ClientConn.
|
||||
func (cc *ClientConn) Name() string { return cc.name }
|
||||
|
||||
func (cc *ClientConn) server() *ServerConn {
|
||||
|
@ -69,6 +71,8 @@ func (cc *ClientConn) server() *ServerConn {
|
|||
return cc.srv
|
||||
}
|
||||
|
||||
// ServerName returns the name of the current upstream server
|
||||
// of the ClientConn. It is empty if there is no upstream connection.
|
||||
func (cc *ClientConn) ServerName() string {
|
||||
srv := cc.server()
|
||||
if srv != nil {
|
||||
|
@ -92,8 +96,12 @@ func (cc *ClientConn) setState(state clientState) {
|
|||
cc.cstate = state
|
||||
}
|
||||
|
||||
// Init returns a channel that is closed
|
||||
// when the ClientConn enters the csActive state.
|
||||
func (cc *ClientConn) Init() <-chan struct{} { return cc.initCh }
|
||||
|
||||
// Log logs an interaction with the ClientConn.
|
||||
// dir indicates the direction of the interaction.
|
||||
func (cc *ClientConn) Log(dir string, v ...interface{}) {
|
||||
if cc.Name() != "" {
|
||||
format := "{%s, %s} %s {←|⇶}"
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*
|
||||
mt-multiserver-proxy starts the reverse proxy.
|
||||
*/
|
||||
package main
|
||||
|
||||
import "github.com/HimbeerserverDE/mt-multiserver-proxy"
|
||||
|
|
13
config.go
13
config.go
|
@ -8,12 +8,6 @@ import (
|
|||
"sync"
|
||||
)
|
||||
|
||||
const latestSerializeVer = 28
|
||||
const latestProtoVer = 39
|
||||
const maxPlayerNameLen = 20
|
||||
const playerNameChars = "^[a-zA-Z0-9-_]+$"
|
||||
const bytesPerMediaBunch = 5000
|
||||
|
||||
const defaultCmdPrefix = ">"
|
||||
const defaultSendInterval = 0.09
|
||||
const defaultUserLimit = 10
|
||||
|
@ -23,6 +17,8 @@ const defaultBindAddr = ":40000"
|
|||
var config Config
|
||||
var configMu sync.RWMutex
|
||||
|
||||
// A Config contains information from the configuration file
|
||||
// that affects the way the proxy works.
|
||||
type Config struct {
|
||||
NoPlugins bool
|
||||
CmdPrefix string
|
||||
|
@ -48,6 +44,8 @@ type Config struct {
|
|||
UserGroups map[string]string
|
||||
}
|
||||
|
||||
// Conf returns a copy of the Config used by the proxy.
|
||||
// Any modifications will not affect the original Config.
|
||||
func Conf() Config {
|
||||
configMu.RLock()
|
||||
defer configMu.RUnlock()
|
||||
|
@ -55,6 +53,9 @@ func Conf() Config {
|
|||
return config
|
||||
}
|
||||
|
||||
// LoadConfig attempts to parse the configuration file.
|
||||
// It leaves the config unchanged if there is an error
|
||||
// and returns the error.
|
||||
func LoadConfig() error {
|
||||
configMu.Lock()
|
||||
defer configMu.Unlock()
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/anon55555/mt"
|
||||
)
|
||||
|
||||
func Connect(conn net.Conn, name string, cc *ClientConn) *ServerConn {
|
||||
func connect(conn net.Conn, name string, cc *ClientConn) *ServerConn {
|
||||
cc.mu.RLock()
|
||||
if cc.srv != nil {
|
||||
cc.Log("<->", "already connected to server")
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//go:build ignore
|
||||
|
||||
// This program generates default_textures.go. It can be invoked
|
||||
// by running go generate
|
||||
// gen_textures generates default_textures.go. It can be invoked
|
||||
// by running go generate.
|
||||
package main
|
||||
|
||||
import (
|
||||
|
@ -23,6 +23,7 @@ func main() {
|
|||
}
|
||||
defer f.Close()
|
||||
|
||||
f.WriteString("// Code generated by gen_textures.go. DO NOT EDIT.\n")
|
||||
f.WriteString("package proxy\n")
|
||||
f.WriteString("\n")
|
||||
f.WriteString("var defaultTextures = []mediaFile{\n")
|
||||
|
|
7
hop.go
7
hop.go
|
@ -8,6 +8,9 @@ import (
|
|||
"github.com/anon55555/mt"
|
||||
)
|
||||
|
||||
// Hop connects the ClientConn to the specified upstream server.
|
||||
// At the moment the ClientConn is NOT fixed if an error occurs
|
||||
// so the player may have to reconnect.
|
||||
func (cc *ClientConn) Hop(serverName string) error {
|
||||
cc.hopMu.Lock()
|
||||
defer cc.hopMu.Unlock()
|
||||
|
@ -32,7 +35,7 @@ func (cc *ClientConn) Hop(serverName string) error {
|
|||
return fmt.Errorf("inexistent server")
|
||||
}
|
||||
|
||||
// This needs to be done before the serverConn is closed
|
||||
// This needs to be done before the ServerConn is closed
|
||||
// so the clientConn isn't closed by the packet handler
|
||||
cc.server().mu.Lock()
|
||||
cc.server().clt = nil
|
||||
|
@ -144,7 +147,7 @@ func (cc *ClientConn) Hop(serverName string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
Connect(conn, serverName, cc)
|
||||
connect(conn, serverName, cc)
|
||||
|
||||
for ch := range cc.modChs {
|
||||
cc.server().SendCmd(&mt.ToSrvJoinModChan{Channel: ch})
|
||||
|
|
|
@ -32,7 +32,7 @@ type listener struct {
|
|||
clts map[*ClientConn]struct{}
|
||||
}
|
||||
|
||||
func Listen(pc net.PacketConn) *listener {
|
||||
func listen(pc net.PacketConn) *listener {
|
||||
l := &listener{
|
||||
Listener: mt.Listen(pc),
|
||||
clts: make(map[*ClientConn]struct{}),
|
||||
|
@ -49,7 +49,7 @@ func Listen(pc net.PacketConn) *listener {
|
|||
return l
|
||||
}
|
||||
|
||||
func (l *listener) Clts() map[*ClientConn]struct{} {
|
||||
func (l *listener) clients() map[*ClientConn]struct{} {
|
||||
clts := make(map[*ClientConn]struct{})
|
||||
|
||||
l.mu.RLock()
|
||||
|
@ -62,7 +62,7 @@ func (l *listener) Clts() map[*ClientConn]struct{} {
|
|||
return clts
|
||||
}
|
||||
|
||||
func (l *listener) Accept() (*ClientConn, error) {
|
||||
func (l *listener) accept() (*ClientConn, error) {
|
||||
p, err := l.Listener.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
2
log.go
2
log.go
|
@ -10,6 +10,8 @@ type LogWriter struct {
|
|||
f *os.File
|
||||
}
|
||||
|
||||
// Write writes the input data to os.Stderr and the log file.
|
||||
// It returns the number of bytes written and an error.
|
||||
func (lw *LogWriter) Write(p []byte) (n int, err error) {
|
||||
n, err = os.Stderr.Write(p)
|
||||
if err != nil {
|
||||
|
|
5
perms.go
5
perms.go
|
@ -1,7 +1,6 @@
|
|||
package proxy
|
||||
|
||||
import ()
|
||||
|
||||
// Perms returns the permissions of the ClientConn.
|
||||
func (cc *ClientConn) Perms() []string {
|
||||
if cc.Name() == "" {
|
||||
return []string{}
|
||||
|
@ -19,6 +18,8 @@ func (cc *ClientConn) Perms() []string {
|
|||
return []string{}
|
||||
}
|
||||
|
||||
// HasPerms returns true if the ClientConn has all
|
||||
// of the specified permissions. Otherwise it returns false.
|
||||
func (cc *ClientConn) HasPerms(want ...string) bool {
|
||||
has := make(map[string]struct{})
|
||||
for _, perm := range cc.Perms() {
|
||||
|
|
|
@ -5,6 +5,8 @@ import "sync"
|
|||
var players = make(map[string]struct{})
|
||||
var playersMu sync.RWMutex
|
||||
|
||||
// Players returns the names of all players
|
||||
// that are currently connected to the proxy.
|
||||
func Players() map[string]struct{} {
|
||||
playersMu.RLock()
|
||||
defer playersMu.RUnlock()
|
||||
|
@ -17,11 +19,12 @@ func Players() map[string]struct{} {
|
|||
return p
|
||||
}
|
||||
|
||||
// Clts returns all ClientConns currently connected to the proxy.
|
||||
func Clts() map[*ClientConn]struct{} {
|
||||
clts := make(map[*ClientConn]struct{})
|
||||
lm := allListeners()
|
||||
for l := range lm {
|
||||
for clt := range l.Clts() {
|
||||
for clt := range l.clients() {
|
||||
clts[clt] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +32,8 @@ func Clts() map[*ClientConn]struct{} {
|
|||
return clts
|
||||
}
|
||||
|
||||
// Find returns the ClientConn that has the specified player name.
|
||||
// If no ClientConn is found, nil is returned.
|
||||
func Find(name string) *ClientConn {
|
||||
for clt := range Clts() {
|
||||
if clt.Name() == name {
|
||||
|
|
|
@ -2,6 +2,7 @@ package proxy
|
|||
|
||||
import "sync"
|
||||
|
||||
// A ChatCmd holds information on how to handle a chat command.
|
||||
type ChatCmd struct {
|
||||
Name string
|
||||
Perm string
|
||||
|
@ -14,6 +15,7 @@ var chatCmds map[string]ChatCmd
|
|||
var chatCmdsMu sync.RWMutex
|
||||
var chatCmdsOnce sync.Once
|
||||
|
||||
// ChatCmds returns a map of all ChatCmds indexed by their names.
|
||||
func ChatCmds() map[string]ChatCmd {
|
||||
initChatCmds()
|
||||
|
||||
|
@ -28,12 +30,14 @@ func ChatCmds() map[string]ChatCmd {
|
|||
return cmds
|
||||
}
|
||||
|
||||
// ChatCmdExists reports if a ChatCmd exists.
|
||||
func ChatCmdExists(name string) bool {
|
||||
cmds := ChatCmds()
|
||||
_, ok := cmds[name]
|
||||
_, ok := ChatCmds()[name]
|
||||
return ok
|
||||
}
|
||||
|
||||
// RegisterChatCmd adds a new ChatCmd. It returns true on success
|
||||
// and false if a command with the same name already exists.
|
||||
func RegisterChatCmd(cmd ChatCmd) bool {
|
||||
initChatCmds()
|
||||
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
Package proxy is a minetest reverse proxy for multiple servers.
|
||||
It also provides an API for plugins.
|
||||
*/
|
||||
package proxy
|
||||
|
||||
const latestSerializeVer = 28
|
||||
const latestProtoVer = 39
|
||||
const maxPlayerNameLen = 20
|
||||
const playerNameChars = "^[a-zA-Z0-9-_]+$"
|
||||
const bytesPerMediaBunch = 5000
|
12
run.go
12
run.go
|
@ -12,6 +12,8 @@ import (
|
|||
"github.com/anon55555/mt"
|
||||
)
|
||||
|
||||
// Run initializes the proxy andstarts the main listener loop.
|
||||
// It blocks forever.
|
||||
func Run() {
|
||||
if err := LoadConfig(); err != nil {
|
||||
log.Fatal("{←|⇶} ", err)
|
||||
|
@ -24,7 +26,7 @@ func Run() {
|
|||
var err error
|
||||
switch Conf().AuthBackend {
|
||||
case "sqlite3":
|
||||
SetAuthBackend(AuthSQLite3{})
|
||||
setAuthBackend(authSQLite3{})
|
||||
default:
|
||||
log.Fatal("{←|⇶} invalid auth backend")
|
||||
}
|
||||
|
@ -39,7 +41,7 @@ func Run() {
|
|||
log.Fatal("{←|⇶} ", err)
|
||||
}
|
||||
|
||||
l := Listen(pc)
|
||||
l := listen(pc)
|
||||
defer l.Close()
|
||||
|
||||
log.Print("{←|⇶} listen ", l.Addr())
|
||||
|
@ -49,7 +51,7 @@ func Run() {
|
|||
signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP)
|
||||
<-sig
|
||||
|
||||
clts := l.Clts()
|
||||
clts := l.clients()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(clts))
|
||||
|
@ -75,7 +77,7 @@ func Run() {
|
|||
}()
|
||||
|
||||
for {
|
||||
cc, err := l.Accept()
|
||||
cc, err := l.accept()
|
||||
if err != nil {
|
||||
if errors.Is(err, net.ErrClosed) {
|
||||
log.Print("{←|⇶} stop listening")
|
||||
|
@ -139,7 +141,7 @@ func Run() {
|
|||
return
|
||||
}
|
||||
|
||||
Connect(conn, Conf().Servers[0].Name, cc)
|
||||
connect(conn, Conf().Servers[0].Name, cc)
|
||||
}()
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/anon55555/mt/rudp"
|
||||
)
|
||||
|
||||
// A ServerConn is a connection to a minetest server.
|
||||
type ServerConn struct {
|
||||
mt.Peer
|
||||
clt *ClientConn
|
||||
|
@ -63,8 +64,12 @@ func (sc *ServerConn) setState(state clientState) {
|
|||
sc.cstate = state
|
||||
}
|
||||
|
||||
// Init returns a channel that is closed
|
||||
// when the ServerConn enters the csActive state.
|
||||
func (sc *ServerConn) Init() <-chan struct{} { return sc.initCh }
|
||||
|
||||
// Log logs an interaction with the ServerConn.
|
||||
// dir indicates the direction of the interaction.
|
||||
func (sc *ServerConn) Log(dir string, v ...interface{}) {
|
||||
if sc.client() != nil {
|
||||
format := "%s {%s}"
|
||||
|
|
Loading…
Reference in New Issue