Document exported code (#49)

master
HimbeerserverDE 2021-09-10 12:47:19 +02:00
parent 04d33fd100
commit 7e72caa092
No known key found for this signature in database
GPG Key ID: 1A651504791E6A8B
17 changed files with 95 additions and 37 deletions

View File

@ -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
}

View File

@ -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()
}

View File

@ -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)"
}

View File

@ -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 {←|⇶}"

View File

@ -1,3 +1,6 @@
/*
mt-multiserver-proxy starts the reverse proxy.
*/
package main
import "github.com/HimbeerserverDE/mt-multiserver-proxy"

View File

@ -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()

View File

@ -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")

View File

@ -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
View File

@ -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})

View File

@ -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
View File

@ -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 {

View File

@ -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() {

View File

@ -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 {

View File

@ -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()

11
proxy.go Normal file
View File

@ -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
View File

@ -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)
}()
}

View File

@ -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}"