From ce6a8413b1f833ef8a925e208d4dc67126c150cf Mon Sep 17 00:00:00 2001 From: HimbeerserverDE Date: Sat, 11 Sep 2021 11:45:25 +0200 Subject: [PATCH] Add ban API --- auth.go | 14 ++++++- auth_sqlite3.go | 97 ++++++++++++++++++++++++++++++++++++++++++++++++- client_conn.go | 6 +++ moderation.go | 13 +++++++ 4 files changed, 127 insertions(+), 3 deletions(-) diff --git a/auth.go b/auth.go index 4feec77..2fefd3b 100644 --- a/auth.go +++ b/auth.go @@ -2,6 +2,7 @@ package proxy import ( "errors" + "net" "time" ) @@ -15,13 +16,24 @@ type user struct { timestamp time.Time } +type ban struct { + addr string + name string +} + type authBackend interface { Exists(name string) bool Passwd(name string) (salt, verifier []byte, err error) SetPasswd(name string, salt, verifier []byte) error Timestamp(name string) (time.Time, error) - Import(data []user) + Import(in []user) Export() ([]user, error) + + Ban(addr, name string) error + Unban(id string) error + Banned(addr *net.IPAddr) bool + ImportBans(in []ban) + ExportBans() ([]ban, error) } func setAuthBackend(ab authBackend) error { diff --git a/auth_sqlite3.go b/auth_sqlite3.go index 4ad9724..0eb4f5b 100644 --- a/auth_sqlite3.go +++ b/auth_sqlite3.go @@ -2,6 +2,8 @@ package proxy import ( "database/sql" + "errors" + "net" "os" "path/filepath" "time" @@ -71,7 +73,7 @@ func (a authSQLite3) Timestamp(name string) (time.Time, error) { return time.Parse("2006-01-02 15:04:05", tstr) } -// Import clears the database and and refills it with the passed +// Import deletes all users and and adds the passed // users. func (a authSQLite3) Import(in []user) { if err := a.init(); err != nil { @@ -129,6 +131,94 @@ func (a authSQLite3) Export() ([]user, error) { return out, nil } +// Ban adds a ban entry for a network address and an associated name. +func (a authSQLite3) Ban(addr, name string) error { + if err := a.init(); err != nil { + return err + } + defer a.close() + + if _, err := a.db.Exec(`INSERT INTO ban (addr, name) VALUES (?, ?);`, addr, name); err != nil { + return err + } + + return nil +} + +// Unban deletes a ban entry. It accepts both network addresses +// and player names. +func (a authSQLite3) Unban(id string) error { + if err := a.init(); err != nil { + return err + } + defer a.close() + + if _, err := a.db.Exec(`DELETE FROM ban WHERE addr = ? OR name = ?;`, id, id); err != nil { + return err + } + + return nil +} + +// Banned reports whether a network address is banned +func (a authSQLite3) Banned(addr *net.IPAddr) bool { + if err := a.init(); err != nil { + return true + } + defer a.close() + + var name string + if err := a.db.QueryRow(`SELECT name FROM ban WHERE addr = ?;`, addr.String()).Scan(&name); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return false + } + + return true + } + + return true +} + +// ImportBans deletes all ban entries and adds the passed entries. +func (a authSQLite3) ImportBans(in []ban) { + if err := a.init(); err != nil { + return + } + defer a.close() + + a.db.Exec(`DELETE FROM ban;`) + for _, b := range in { + a.Ban(b.addr, b.name) + } +} + +// ExportBans returns data that can be processed by ImportBans +// or an error. +func (a authSQLite3) ExportBans() ([]ban, error) { + if err := a.init(); err != nil { + return nil, err + } + defer a.close() + + rows, err := a.db.Query(`SELECT * FROM ban;`) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []ban + for rows.Next() { + var b ban + if err := rows.Scan(&b.addr, &b.name); err != nil { + return nil, err + } + + out = append(out, b) + } + + return out, nil +} + func (a authSQLite3) updateTimestamp(name string) { a.db.Exec(`UPDATE user SET timestamp = datetime("now") WHERE name = ?;`, name) } @@ -149,7 +239,10 @@ func (a *authSQLite3) init() error { name VARCHAR(20) PRIMARY KEY NOT NULL, salt BLOB NOT NULL, verifier BLOB NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP);` + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP); +CREATE TABLE IF NOT EXISTS ban ( + addr VARCHAR(39) PRIMARY KEY NOT NULL, + name VARCHAR(20));` if _, err := a.db.Exec(init); err != nil { return err diff --git a/client_conn.go b/client_conn.go index 1d919da..d52f14a 100644 --- a/client_conn.go +++ b/client_conn.go @@ -218,6 +218,12 @@ func handleClt(cc *ClientConn) { cc.name = cmd.PlayerName + if authIface.Banned(cc.RemoteAddr().(*net.IPAddr)) { + cc.Log("<--", "banned") + cc.Kick("Banned by proxy.") + break + } + playersMu.Lock() _, ok := players[cc.Name()] if ok { diff --git a/moderation.go b/moderation.go index 2b21793..e0e3d1a 100644 --- a/moderation.go +++ b/moderation.go @@ -18,3 +18,16 @@ func (cc *ClientConn) Kick(reason string) { } }() } + +// Ban disconnects the ClientConn and prevents the underlying +// network address from connecting again. +func (cc *ClientConn) Ban() error { + cc.Kick("Banned by proxy.") + return authIface.Ban(cc.RemoteAddr().String(), cc.name) +} + +// Unban removes a player from the ban list. It accepts both +// network addresses and player names. +func Unban(id string) error { + return authIface.Unban(id) +}