diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | activeobject.go | 6 | ||||
-rw-r--r-- | auth.go | 21 | ||||
-rw-r--r-- | auth_sqlite3.go | 22 | ||||
-rw-r--r-- | client_conn.go | 136 | ||||
-rw-r--r-- | cmd/mt-multiserver-proxy/main.go (renamed from main.go) | 51 | ||||
-rw-r--r-- | config.go | 6 | ||||
-rw-r--r-- | connect.go | 16 | ||||
-rw-r--r-- | content.go | 16 | ||||
-rw-r--r-- | default_textures.go | 2 | ||||
-rw-r--r-- | formspec.go | 4 | ||||
-rw-r--r-- | gen_textures.go | 2 | ||||
-rw-r--r-- | hop.go | 12 | ||||
-rw-r--r-- | listen.go | 41 | ||||
-rw-r--r-- | log.go | 2 | ||||
-rw-r--r-- | players.go | 2 | ||||
-rw-r--r-- | plugin.go | 4 | ||||
-rw-r--r-- | server_conn.go | 54 |
18 files changed, 211 insertions, 188 deletions
@@ -4,7 +4,7 @@ multiple Minetest servers together. It is the successor to multiserver. ## Installation Go 1.17 or higher is required. Run -`go install github.com/HimbeerserverDE/mt-multiserver-proxy` +`go install github.com/HimbeerserverDE/mt-multiserver-proxy/cmd/mt-multiserver/proxy` to download and compile the project. A mt-multiserver-proxy executable will be created in your $GOBIN directory. diff --git a/activeobject.go b/activeobject.go index 7bba616..aacd95f 100644 --- a/activeobject.go +++ b/activeobject.go @@ -1,8 +1,8 @@ -package main +package proxy import "github.com/anon55555/mt" -func (sc *serverConn) swapAOID(ao *mt.AOID) { +func (sc *ServerConn) swapAOID(ao *mt.AOID) { if sc.client() != nil { if *ao == sc.client().playerCAO { *ao = sc.client().currentCAO @@ -12,7 +12,7 @@ func (sc *serverConn) swapAOID(ao *mt.AOID) { } } -func (sc *serverConn) handleAOMsg(aoMsg mt.AOMsg) { +func (sc *ServerConn) handleAOMsg(aoMsg mt.AOMsg) { switch msg := aoMsg.(type) { case *mt.AOCmdAttach: sc.swapAOID(&msg.Attach.ParentID) @@ -1,8 +1,12 @@ -package main +package proxy -import "time" +import ( + "errors" + "time" +) -var authIface authBackend +var authIface AuthBackend +var ErrAuthBackendExists = errors.New("auth backend already set") type user struct { name string @@ -11,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 @@ -19,3 +23,12 @@ type authBackend interface { Import(data []user) Export() ([]user, error) } + +func SetAuthBackend(ab AuthBackend) error { + if authIface != nil { + return ErrAuthBackendExists + } + + authIface = ab + return nil +} diff --git a/auth_sqlite3.go b/auth_sqlite3.go index 84dcf83..8574768 100644 --- a/auth_sqlite3.go +++ b/auth_sqlite3.go @@ -1,4 +1,4 @@ -package main +package proxy import ( "database/sql" @@ -9,11 +9,11 @@ import ( _ "github.com/mattn/go-sqlite3" ) -type authSQLite3 struct { +type AuthSQLite3 struct { db *sql.DB } -func (a authSQLite3) Exists(name string) bool { +func (a AuthSQLite3) Exists(name string) bool { if err := a.init(); err != nil { return false } @@ -24,7 +24,7 @@ func (a authSQLite3) Exists(name string) bool { return err == nil } -func (a authSQLite3) Passwd(name string) (salt, verifier []byte, err error) { +func (a AuthSQLite3) Passwd(name string) (salt, verifier []byte, err error) { if err = a.init(); err != nil { return } @@ -35,7 +35,7 @@ func (a authSQLite3) Passwd(name string) (salt, verifier []byte, err error) { return } -func (a authSQLite3) SetPasswd(name string, salt, verifier []byte) error { +func (a AuthSQLite3) SetPasswd(name string, salt, verifier []byte) error { if err := a.init(); err != nil { return err } @@ -50,7 +50,7 @@ func (a authSQLite3) SetPasswd(name string, salt, verifier []byte) error { return nil } -func (a authSQLite3) Timestamp(name string) (time.Time, error) { +func (a AuthSQLite3) Timestamp(name string) (time.Time, error) { if err := a.init(); err != nil { return time.Time{}, err } @@ -65,7 +65,7 @@ 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) { +func (a AuthSQLite3) Import(in []user) { if err := a.init(); err != nil { return } @@ -78,7 +78,7 @@ func (a authSQLite3) Import(in []user) { } } -func (a authSQLite3) Export() ([]user, error) { +func (a AuthSQLite3) Export() ([]user, error) { if err := a.init(); err != nil { return nil, err } @@ -119,11 +119,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 +148,6 @@ func (a *authSQLite3) init() error { return nil } -func (a authSQLite3) close() error { +func (a AuthSQLite3) close() error { return a.db.Close() } diff --git a/client_conn.go b/client_conn.go index 4898518..9278578 100644 --- a/client_conn.go +++ b/client_conn.go @@ -1,4 +1,4 @@ -package main +package proxy import ( "crypto/subtle" @@ -24,9 +24,9 @@ const ( csSudo ) -type clientConn struct { +type ClientConn struct { mt.Peer - srv *serverConn + srv *ServerConn mu sync.RWMutex cstate clientState @@ -61,30 +61,30 @@ type clientConn struct { modChs map[string]struct{} } -func (cc *clientConn) server() *serverConn { +func (cc *ClientConn) server() *ServerConn { cc.mu.RLock() defer cc.mu.RUnlock() return cc.srv } -func (cc *clientConn) state() clientState { +func (cc *ClientConn) state() clientState { cc.cstateMu.RLock() defer cc.cstateMu.RUnlock() return cc.cstate } -func (cc *clientConn) setState(state clientState) { +func (cc *ClientConn) setState(state clientState) { cc.cstateMu.Lock() defer cc.cstateMu.Unlock() cc.cstate = state } -func (cc *clientConn) init() <-chan struct{} { return cc.initCh } +func (cc *ClientConn) Init() <-chan struct{} { return cc.initCh } -func (cc *clientConn) log(dir string, v ...interface{}) { +func (cc *ClientConn) Log(dir string, v ...interface{}) { if cc.name != "" { format := "{%s, %s} %s {←|⇶}" format += strings.Repeat(" %v", len(v)) @@ -102,15 +102,15 @@ func (cc *clientConn) log(dir string, v ...interface{}) { } } -func handleClt(cc *clientConn) { +func handleClt(cc *ClientConn) { for { pkt, err := cc.Recv() if err != nil { if errors.Is(err, net.ErrClosed) { if errors.Is(cc.WhyClosed(), rudp.ErrTimedOut) { - cc.log("<->", "timeout") + cc.Log("<->", "timeout") } else { - cc.log("<->", "disconnect") + cc.Log("<->", "disconnect") } if cc.name != "" { @@ -134,20 +134,20 @@ func handleClt(cc *clientConn) { break } - cc.log("-->", err) + cc.Log("-->", err) continue } switch cmd := pkt.Cmd.(type) { case *mt.ToSrvInit: if cc.state() > csCreated { - cc.log("-->", "duplicate init") + cc.Log("-->", "duplicate init") break } cc.setState(csInit) if cmd.SerializeVer != latestSerializeVer { - cc.log("<--", "invalid serializeVer") + cc.Log("<--", "invalid serializeVer") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.UnsupportedVer}) select { @@ -160,7 +160,7 @@ func handleClt(cc *clientConn) { } if cmd.MaxProtoVer < latestProtoVer { - cc.log("<--", "invalid protoVer") + cc.Log("<--", "invalid protoVer") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.UnsupportedVer}) select { @@ -173,7 +173,7 @@ func handleClt(cc *clientConn) { } if len(cmd.PlayerName) == 0 || len(cmd.PlayerName) > maxPlayerNameLen { - cc.log("<--", "invalid player name length") + cc.Log("<--", "invalid player name length") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.BadName}) select { @@ -186,7 +186,7 @@ func handleClt(cc *clientConn) { } if ok, _ := regexp.MatchString(playerNameChars, cmd.PlayerName); !ok { - cc.log("<--", "invalid player name") + cc.Log("<--", "invalid player name") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.BadNameChars}) select { @@ -203,7 +203,7 @@ func handleClt(cc *clientConn) { playersMu.Lock() _, ok := players[cc.name] if ok { - cc.log("<--", "already connected") + cc.Log("<--", "already connected") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.AlreadyConnected}) select { @@ -220,7 +220,7 @@ func handleClt(cc *clientConn) { playersMu.Unlock() if cc.name == "singleplayer" { - cc.log("<--", "name is singleplayer") + cc.Log("<--", "name is singleplayer") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.BadName}) select { @@ -233,8 +233,8 @@ func handleClt(cc *clientConn) { } // user limit - if len(players) >= conf().UserLimit { - cc.log("<--", "player limit reached") + if len(players) >= Conf().UserLimit { + cc.Log("<--", "player limit reached") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.TooManyClts}) select { @@ -262,7 +262,7 @@ func handleClt(cc *clientConn) { case *mt.ToSrvFirstSRP: if cc.state() == csInit { if cc.auth.method != mt.FirstSRP { - cc.log("-->", "unauthorized password change") + cc.Log("-->", "unauthorized password change") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.UnexpectedData}) select { @@ -279,8 +279,8 @@ func handleClt(cc *clientConn) { salt, srpA, srpB, srpM, srpK []byte }{} - if cmd.EmptyPasswd && conf().RequirePasswd { - cc.log("<--", "empty password disallowed") + if cmd.EmptyPasswd && Conf().RequirePasswd { + cc.Log("<--", "empty password disallowed") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.EmptyPasswd}) select { @@ -293,7 +293,7 @@ func handleClt(cc *clientConn) { } if err := authIface.SetPasswd(cc.name, cmd.Salt, cmd.Verifier); err != nil { - cc.log("<--", "set password fail") + cc.Log("<--", "set password fail") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.SrvErr}) select { @@ -305,22 +305,22 @@ func handleClt(cc *clientConn) { break } - cc.log("-->", "set password") + cc.Log("-->", "set password") cc.SendCmd(&mt.ToCltAcceptAuth{ PlayerPos: mt.Pos{0, 5, 0}, MapSeed: 0, - SendInterval: conf().SendInterval, + SendInterval: Conf().SendInterval, SudoAuthMethods: mt.SRP, }) } else { if cc.state() < csSudo { - cc.log("-->", "unauthorized sudo action") + cc.Log("-->", "unauthorized sudo action") break } cc.setState(cc.state() - 1) if err := authIface.SetPasswd(cc.name, cmd.Salt, cmd.Verifier); err != nil { - cc.log("<--", "change password fail") + cc.Log("<--", "change password fail") cc.SendCmd(&mt.ToCltChatMsg{ Type: mt.SysMsg, Text: "Password change failed or unavailable.", @@ -329,7 +329,7 @@ func handleClt(cc *clientConn) { break } - cc.log("-->", "change password") + cc.Log("-->", "change password") cc.SendCmd(&mt.ToCltChatMsg{ Type: mt.SysMsg, Text: "Password change successful.", @@ -340,12 +340,12 @@ func handleClt(cc *clientConn) { wantSudo := cc.state() == csActive if cc.state() != csInit && cc.state() != csActive { - cc.log("-->", "unexpected authentication") + cc.Log("-->", "unexpected authentication") break } if !wantSudo && cc.auth.method != mt.SRP { - cc.log("<--", "multiple authentication attempts") + cc.Log("<--", "multiple authentication attempts") if wantSudo { cc.SendCmd(&mt.ToCltDenySudoMode{}) break @@ -362,7 +362,7 @@ func handleClt(cc *clientConn) { } if !cmd.NoSHA1 { - cc.log("<--", "unsupported SHA1 auth") + cc.Log("<--", "unsupported SHA1 auth") break } @@ -370,7 +370,7 @@ func handleClt(cc *clientConn) { salt, verifier, err := authIface.Passwd(cc.name) if err != nil { - cc.log("<--", "SRP data retrieval fail") + cc.Log("<--", "SRP data retrieval fail") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.SrvErr}) select { @@ -386,7 +386,7 @@ func handleClt(cc *clientConn) { cc.auth.srpA = cmd.A cc.auth.srpB, _, cc.auth.srpK, err = srp.Handshake(cc.auth.srpA, verifier) if err != nil || cc.auth.srpB == nil { - cc.log("<--", "SRP safety check fail") + cc.Log("<--", "SRP safety check fail") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.UnexpectedData}) select { @@ -406,12 +406,12 @@ func handleClt(cc *clientConn) { wantSudo := cc.state() == csActive if cc.state() != csInit && cc.state() != csActive { - cc.log("-->", "unexpected authentication") + cc.Log("-->", "unexpected authentication") break } if cc.auth.method != mt.SRP { - cc.log("<--", "multiple authentication attempts") + cc.Log("<--", "multiple authentication attempts") if wantSudo { cc.SendCmd(&mt.ToCltDenySudoMode{}) break @@ -442,18 +442,18 @@ func handleClt(cc *clientConn) { cc.SendCmd(&mt.ToCltAcceptAuth{ PlayerPos: mt.Pos{0, 5, 0}, MapSeed: 0, - SendInterval: conf().SendInterval, + SendInterval: Conf().SendInterval, SudoAuthMethods: mt.SRP, }) } } else { if wantSudo { - cc.log("<--", "invalid password (sudo)") + cc.Log("<--", "invalid password (sudo)") cc.SendCmd(&mt.ToCltDenySudoMode{}) break } - cc.log("<--", "invalid password") + cc.Log("<--", "invalid password") ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.WrongPasswd}) select { @@ -467,7 +467,7 @@ func handleClt(cc *clientConn) { case *mt.ToSrvInit2: cc.itemDefs, cc.aliases, cc.nodeDefs, cc.p0Map, cc.p0SrvMap, cc.media, err = muxContent(cc.name) if err != nil { - cc.log("<--", err.Error()) + cc.Log("<--", err.Error()) ack, _ := cc.SendCmd(&mt.ToCltDisco{ Reason: mt.Custom, @@ -502,28 +502,28 @@ func handleClt(cc *clientConn) { cc.lang = cmd.Lang var csmrf mt.CSMRestrictionFlags - if conf().CSMRF.NoCSMs { + if Conf().CSMRF.NoCSMs { csmrf |= mt.NoCSMs } - if !conf().CSMRF.ChatMsgs { + if !Conf().CSMRF.ChatMsgs { csmrf |= mt.NoChatMsgs } - if !conf().CSMRF.ItemDefs { + if !Conf().CSMRF.ItemDefs { csmrf |= mt.NoItemDefs } - if !conf().CSMRF.NodeDefs { + if !Conf().CSMRF.NodeDefs { csmrf |= mt.NoNodeDefs } - if !conf().CSMRF.NoLimitMapRange { + if !Conf().CSMRF.NoLimitMapRange { csmrf |= mt.LimitMapRange } - if !conf().CSMRF.PlayerList { + if !Conf().CSMRF.PlayerList { csmrf |= mt.NoPlayerList } cc.SendCmd(&mt.ToCltCSMRestrictionFlags{ Flags: csmrf, - MapRange: conf().MapRange, + MapRange: Conf().MapRange, }) case *mt.ToSrvReqMedia: cc.sendMedia(cmd.Filenames) @@ -539,7 +539,7 @@ func handleClt(cc *clientConn) { close(cc.initCh) case *mt.ToSrvInteract: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } @@ -550,32 +550,32 @@ func handleClt(cc *clientConn) { cc.server().SendCmd(cmd) case *mt.ToSrvChatMsg: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } var handled bool - if strings.HasPrefix(cmd.Msg, conf().CmdPrefix) { + if strings.HasPrefix(cmd.Msg, Conf().CmdPrefix) { substrs := strings.Split(cmd.Msg, " ") - cmdName := strings.Replace(substrs[0], conf().CmdPrefix, "", 1) + cmdName := strings.Replace(substrs[0], Conf().CmdPrefix, "", 1) var args []string if len(substrs) > 1 { args = substrs[1:] } - cc.log("-->", append([]string{"cmd", cmdName}, args...)) + cc.Log("-->", append([]string{"cmd", cmdName}, args...)) pluginsMu.RLock() for _, p := range plugins { sym, err := p.Lookup("HandleChatCmd") if err != nil { - cc.log("-->", err) + cc.Log("-->", err) continue } - if handler, ok := sym.(func(string, []string) bool); ok { - if handler(cmdName, args) { + if handler, ok := sym.(func(*ClientConn, string, ...string) bool); ok { + if handler(cc, cmdName, args...) { handled = true break } @@ -598,73 +598,73 @@ func handleClt(cc *clientConn) { } case *mt.ToSrvDeletedBlks: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvFallDmg: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvGotBlks: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvJoinModChan: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvLeaveModChan: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvMsgModChan: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvNodeMetaFields: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvPlayerPos: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvRespawn: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvInvAction: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvInvFields: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) case *mt.ToSrvSelectItem: if cc.server() == nil { - cc.log("-->", "no server") + cc.Log("-->", "no server") break } cc.server().SendCmd(cmd) diff --git a/main.go b/cmd/mt-multiserver-proxy/main.go index c18b387..e1b2b19 100644 --- a/main.go +++ b/cmd/mt-multiserver-proxy/main.go @@ -9,29 +9,30 @@ import ( "sync" "syscall" + "github.com/HimbeerserverDE/mt-multiserver-proxy" "github.com/anon55555/mt" ) func main() { - if err := loadConfig(); err != nil { + if err := proxy.LoadConfig(); err != nil { log.Fatal("{←|⇶} ", err) } - if !conf().NoPlugins { - if err := loadPlugins(); err != nil { + if !proxy.Conf().NoPlugins { + if err := proxy.LoadPlugins(); err != nil { log.Fatal("{←|⇶} ", err) } } var err error - switch conf().AuthBackend { + switch proxy.Conf().AuthBackend { case "sqlite3": - authIface = authSQLite3{} + proxy.SetAuthBackend(proxy.AuthSQLite3{}) default: log.Fatal("{←|⇶} invalid auth backend") } - addr, err := net.ResolveUDPAddr("udp", conf().BindAddr) + addr, err := net.ResolveUDPAddr("udp", proxy.Conf().BindAddr) if err != nil { log.Fatal("{←|⇶} ", err) } @@ -41,24 +42,23 @@ func main() { log.Fatal("{←|⇶} ", err) } - l := listen(pc) - defer l.close() + l := proxy.Listen(pc) + defer l.Close() - log.Print("{←|⇶} listen ", l.addr()) + log.Print("{←|⇶} listen ", l.Addr()) go func() { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP) <-sig - l.mu.Lock() - defer l.mu.Unlock() + clts := l.Clts() var wg sync.WaitGroup - wg.Add(len(l.clts)) + wg.Add(len(clts)) - for cc := range l.clts { - go func(cc *clientConn) { + for cc := range clts { + go func(cc *proxy.ClientConn) { ack, _ := cc.SendCmd(&mt.ToCltDisco{Reason: mt.Shutdown}) select { case <-cc.Closed(): @@ -66,11 +66,6 @@ func main() { cc.Close() } - <-cc.server().Closed() - cc.mu.Lock() - cc.srv = nil - cc.mu.Unlock() - wg.Done() }(cc) } @@ -80,7 +75,7 @@ func main() { }() for { - cc, err := l.accept() + cc, err := l.Accept() if err != nil { if errors.Is(err, net.ErrClosed) { log.Print("{←|⇶} stop listening") @@ -92,11 +87,11 @@ func main() { } go func() { - <-cc.init() - cc.log("<->", "handshake completed") + <-cc.Init() + cc.Log("<->", "handshake completed") - if len(conf().Servers) == 0 { - cc.log("<--", "no servers") + if len(proxy.Conf().Servers) == 0 { + cc.Log("<--", "no servers") ack, _ := cc.SendCmd(&mt.ToCltDisco{ Reason: mt.Custom, Custom: "No servers are configured.", @@ -110,9 +105,9 @@ func main() { return } - addr, err := net.ResolveUDPAddr("udp", conf().Servers[0].Addr) + addr, err := net.ResolveUDPAddr("udp", proxy.Conf().Servers[0].Addr) if err != nil { - cc.log("<--", "address resolution fail") + cc.Log("<--", "address resolution fail") ack, _ := cc.SendCmd(&mt.ToCltDisco{ Reason: mt.Custom, Custom: "Server address resolution failed.", @@ -128,7 +123,7 @@ func main() { conn, err := net.DialUDP("udp", nil, addr) if err != nil { - cc.log("<--", "connection fail") + cc.Log("<--", "connection fail") ack, _ := cc.SendCmd(&mt.ToCltDisco{ Reason: mt.Custom, @@ -144,7 +139,7 @@ func main() { return } - connect(conn, conf().Servers[0].Name, cc) + proxy.Connect(conn, proxy.Conf().Servers[0].Name, cc) }() } @@ -1,4 +1,4 @@ -package main +package proxy import ( "encoding/json" @@ -46,14 +46,14 @@ type Config struct { MapRange uint32 } -func conf() Config { +func Conf() Config { configMu.RLock() defer configMu.RUnlock() return config } -func loadConfig() error { +func LoadConfig() error { configMu.Lock() defer configMu.Unlock() @@ -1,4 +1,4 @@ -package main +package proxy import ( "net" @@ -6,8 +6,16 @@ import ( "github.com/anon55555/mt" ) -func connect(conn net.Conn, name string, cc *clientConn) *serverConn { - sc := &serverConn{ +func Connect(conn net.Conn, name string, cc *ClientConn) *ServerConn { + cc.mu.RLock() + if cc.srv != nil { + cc.Log("<->", "already connected to server") + cc.mu.RUnlock() + return nil + } + cc.mu.RUnlock() + + sc := &ServerConn{ Peer: mt.Connect(conn), initCh: make(chan struct{}), clt: cc, @@ -18,7 +26,7 @@ func connect(conn net.Conn, name string, cc *clientConn) *serverConn { huds: make(map[mt.HUDID]mt.HUDType), playerList: make(map[string]struct{}), } - sc.log("-->", "connect") + sc.Log("-->", "connect") cc.mu.Lock() cc.srv = sc @@ -1,4 +1,4 @@ -package main +package proxy import ( "encoding/base64" @@ -235,7 +235,7 @@ func handleContent(cc *contentConn) { } } -func (cc *clientConn) sendMedia(filenames []string) { +func (cc *ClientConn) sendMedia(filenames []string) { var bunches [][]struct { Name string Data []byte @@ -274,7 +274,7 @@ func (cc *clientConn) sendMedia(filenames []string) { } if !known { - cc.log("-->", "request unknown media file") + cc.Log("-->", "request unknown media file") continue } } @@ -435,7 +435,7 @@ func muxMedia(conns []*contentConn) []mediaFile { func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias, Orig string }, nodeDefs []mt.NodeDef, p0Map param0Map, p0SrvMap param0SrvMap, media []mediaFile, err error) { var conns []*contentConn - for _, srv := range conf().Servers { + for _, srv := range Conf().Servers { var addr *net.UDPAddr addr, err = net.ResolveUDPAddr("udp", srv.Addr) if err != nil { @@ -464,7 +464,7 @@ func muxContent(userName string) (itemDefs []mt.ItemDef, aliases []struct{ Alias return } -func (sc *serverConn) globalParam0(p0 *mt.Content) { +func (sc *ServerConn) globalParam0(p0 *mt.Content) { clt := sc.client() if clt != nil && clt.p0Map != nil { if clt.p0Map[sc.name] != nil { @@ -473,7 +473,7 @@ func (sc *serverConn) globalParam0(p0 *mt.Content) { } } -func (cc *clientConn) srvParam0(p0 *mt.Content) string { +func (cc *ClientConn) srvParam0(p0 *mt.Content) string { if cc.p0SrvMap != nil { srv := cc.p0SrvMap[*p0] *p0 = srv.param0 @@ -532,7 +532,7 @@ func prependTexture(prep string, t *mt.Texture) { *t = mt.Texture(s) } -func (sc *serverConn) prependInv(inv mt.Inv) { +func (sc *ServerConn) prependInv(inv mt.Inv) { for k, l := range inv { for i := range l.Stacks { prepend(sc.name, &inv[k].InvList.Stacks[i].Name) @@ -540,7 +540,7 @@ func (sc *serverConn) prependInv(inv mt.Inv) { } } -func (sc *serverConn) prependHUD(t mt.HUDType, cmdIface mt.ToCltCmd) { +func (sc *ServerConn) prependHUD(t mt.HUDType, cmdIface mt.ToCltCmd) { pa := func(cmd *mt.ToCltAddHUD) { switch t { case mt.StatbarHUD: diff --git a/default_textures.go b/default_textures.go index 7cc5330..148868c 100644 --- a/default_textures.go +++ b/default_textures.go @@ -1,4 +1,4 @@ -package main +package proxy var defaultTextures = []mediaFile{ mediaFile{ diff --git a/formspec.go b/formspec.go index 5a96b74..12553f1 100644 --- a/formspec.go +++ b/formspec.go @@ -1,11 +1,11 @@ -package main +package proxy import ( "regexp" "strings" ) -func (sc *serverConn) prependFormspec(fs *string) { +func (sc *ServerConn) prependFormspec(fs *string) { reg := regexp.MustCompile("[^a-zA-Z0-9-_.:]") reg2 := regexp.MustCompile("[a-zA-Z0-9-_.]*\\.[a-zA-Z-_.]+") subs := reg.Split(*fs, -1) diff --git a/gen_textures.go b/gen_textures.go index 3712096..f2a9517 100644 --- a/gen_textures.go +++ b/gen_textures.go @@ -23,7 +23,7 @@ func main() { } defer f.Close() - f.WriteString("package main\n") + f.WriteString("package proxy\n") f.WriteString("\n") f.WriteString("var defaultTextures = []mediaFile{\n") @@ -1,4 +1,4 @@ -package main +package proxy import ( "fmt" @@ -8,20 +8,20 @@ import ( "github.com/anon55555/mt" ) -func (cc *clientConn) hop(serverName string) error { +func (cc *ClientConn) hop(serverName string) error { cc.hopMu.Lock() defer cc.hopMu.Unlock() - cc.log("<->", "hop", serverName) + cc.Log("<->", "hop", serverName) if cc.server() == nil { err := fmt.Errorf("no server connection") - cc.log("<->", err) + cc.Log("<->", err) return err } var strAddr string - for _, srv := range conf().Servers { + for _, srv := range Conf().Servers { if srv.Name == serverName { strAddr = srv.Addr break @@ -145,7 +145,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}) @@ -1,4 +1,4 @@ -package main +package proxy import ( "fmt" @@ -8,33 +8,40 @@ import ( "github.com/anon55555/mt" ) -type listener struct { - mtListener mt.Listener - mu sync.Mutex +type Listener struct { + mt.Listener + mu sync.RWMutex - clts map[*clientConn]struct{} + clts map[*ClientConn]struct{} } -func listen(pc net.PacketConn) *listener { - return &listener{ - mtListener: mt.Listen(pc), - clts: make(map[*clientConn]struct{}), +func Listen(pc net.PacketConn) *Listener { + return &Listener{ + Listener: mt.Listen(pc), + clts: make(map[*ClientConn]struct{}), } } -func (l *listener) close() error { - return l.mtListener.Close() -} +func (l *Listener) Clts() map[*ClientConn]struct{} { + clts := make(map[*ClientConn]struct{}) + + l.mu.RLock() + defer l.mu.RUnlock() -func (l *listener) addr() net.Addr { return l.mtListener.Addr() } + for cc := range l.clts { + clts[cc] = struct{}{} + } + + return clts +} -func (l *listener) accept() (*clientConn, error) { - p, err := l.mtListener.Accept() +func (l *Listener) Accept() (*ClientConn, error) { + p, err := l.Listener.Accept() if err != nil { return nil, err } - cc := &clientConn{ + cc := &ClientConn{ Peer: p, initCh: make(chan struct{}), modChs: make(map[string]struct{}), @@ -52,7 +59,7 @@ func (l *listener) accept() (*clientConn, error) { delete(l.clts, cc) }() - cc.log("-->", "connect") + cc.Log("-->", "connect") go handleClt(cc) select { @@ -1,4 +1,4 @@ -package main +package proxy import ( "log" @@ -1,4 +1,4 @@ -package main +package proxy import "sync" @@ -1,4 +1,4 @@ -package main +package proxy import ( "log" @@ -11,7 +11,7 @@ import ( var plugins []*plugin.Plugin var pluginsMu sync.RWMutex -func loadPlugins() error { +func LoadPlugins() error { executable, err := os.Executable() if err != nil { return err diff --git a/server_conn.go b/server_conn.go index 673429f..bc89061 100644 --- a/server_conn.go +++ b/server_conn.go @@ -1,4 +1,4 @@ -package main +package proxy import ( "errors" @@ -14,9 +14,9 @@ import ( "github.com/anon55555/mt/rudp" ) -type serverConn struct { +type ServerConn struct { mt.Peer - clt *clientConn + clt *ClientConn mu sync.RWMutex cstate clientState @@ -42,35 +42,35 @@ type serverConn struct { playerList map[string]struct{} } -func (sc *serverConn) client() *clientConn { +func (sc *ServerConn) client() *ClientConn { sc.mu.RLock() defer sc.mu.RUnlock() return sc.clt } -func (sc *serverConn) state() clientState { +func (sc *ServerConn) state() clientState { sc.cstateMu.RLock() defer sc.cstateMu.RUnlock() return sc.cstate } -func (sc *serverConn) setState(state clientState) { +func (sc *ServerConn) setState(state clientState) { sc.cstateMu.Lock() defer sc.cstateMu.Unlock() sc.cstate = state } -func (sc *serverConn) init() <-chan struct{} { return sc.initCh } +func (sc *ServerConn) Init() <-chan struct{} { return sc.initCh } -func (sc *serverConn) log(dir string, v ...interface{}) { +func (sc *ServerConn) Log(dir string, v ...interface{}) { if sc.client() != nil { format := "%s {%s}" format += strings.Repeat(" %v", len(v)) - sc.client().log("", fmt.Sprintf(format, append([]interface{}{ + sc.client().Log("", fmt.Sprintf(format, append([]interface{}{ dir, sc.name, }, v...)...)) @@ -82,7 +82,7 @@ func (sc *serverConn) log(dir string, v ...interface{}) { } } -func handleSrv(sc *serverConn) { +func handleSrv(sc *ServerConn) { go func() { init := make(chan struct{}) defer close(init) @@ -91,7 +91,7 @@ func handleSrv(sc *serverConn) { select { case <-init: case <-time.After(10 * time.Second): - sc.log("-->", "timeout") + sc.Log("-->", "timeout") sc.Close() } }(init) @@ -112,9 +112,9 @@ func handleSrv(sc *serverConn) { if err != nil { if errors.Is(err, net.ErrClosed) { if errors.Is(sc.WhyClosed(), rudp.ErrTimedOut) { - sc.log("<->", "timeout") + sc.Log("<->", "timeout") } else { - sc.log("<->", "disconnect") + sc.Log("<->", "disconnect") } if sc.client() != nil { @@ -141,20 +141,20 @@ func handleSrv(sc *serverConn) { break } - sc.log("<--", err) + sc.Log("<--", err) continue } clt := sc.client() if clt == nil { - sc.log("<--", "no client") + sc.Log("<--", "no client") continue } switch cmd := pkt.Cmd.(type) { case *mt.ToCltHello: if sc.auth.method != 0 { - sc.log("<--", "unexpected authentication") + sc.Log("<--", "unexpected authentication") sc.Close() break } @@ -167,7 +167,7 @@ func handleSrv(sc *serverConn) { } if cmd.SerializeVer != latestSerializeVer { - sc.log("<--", "invalid serializeVer") + sc.Log("<--", "invalid serializeVer") break } @@ -175,7 +175,7 @@ func handleSrv(sc *serverConn) { case mt.SRP: sc.auth.srpA, sc.auth.a, err = srp.InitiateHandshake() if err != nil { - sc.log("-->", err) + sc.Log("-->", err) break } @@ -186,7 +186,7 @@ func handleSrv(sc *serverConn) { case mt.FirstSRP: salt, verifier, err := srp.NewClient([]byte(clt.name), []byte{}) if err != nil { - sc.log("-->", err) + sc.Log("-->", err) break } @@ -196,24 +196,24 @@ func handleSrv(sc *serverConn) { EmptyPasswd: true, }) default: - sc.log("<->", "invalid auth method") + sc.Log("<->", "invalid auth method") sc.Close() } case *mt.ToCltSRPBytesSaltB: if sc.auth.method != mt.SRP { - sc.log("<--", "multiple authentication attempts") + sc.Log("<--", "multiple authentication attempts") break } sc.auth.srpK, err = srp.CompleteHandshake(sc.auth.srpA, sc.auth.a, []byte(clt.name), []byte{}, cmd.Salt, cmd.B) if err != nil { - sc.log("-->", err) + sc.Log("-->", err) break } M := srp.ClientProof([]byte(clt.name), cmd.Salt, sc.auth.srpA, cmd.B, sc.auth.srpK) if M == nil { - sc.log("<--", "SRP safety check fail") + sc.Log("<--", "SRP safety check fail") break } @@ -221,7 +221,7 @@ func handleSrv(sc *serverConn) { M: M, }) case *mt.ToCltDisco: - sc.log("<--", "deny access", cmd) + sc.Log("<--", "deny access", cmd) ack, _ := clt.SendCmd(cmd) select { @@ -240,9 +240,9 @@ func handleSrv(sc *serverConn) { }{} sc.SendCmd(&mt.ToSrvInit2{Lang: clt.lang}) case *mt.ToCltDenySudoMode: - sc.log("<--", "deny sudo") + sc.Log("<--", "deny sudo") case *mt.ToCltAcceptSudoMode: - sc.log("<--", "accept sudo") + sc.Log("<--", "accept sudo") sc.setState(sc.state() + 1) case *mt.ToCltAnnounceMedia: sc.SendCmd(&mt.ToSrvReqMedia{}) @@ -256,7 +256,7 @@ func handleSrv(sc *serverConn) { Formspec: clt.formspecVer, }) - sc.log("<->", "handshake completed") + sc.Log("<->", "handshake completed") sc.setState(sc.state() + 1) close(sc.initCh) case *mt.ToCltInv: |