From e7ada7435f007075d7f07c50c9c72287b76edb5c Mon Sep 17 00:00:00 2001 From: Jacob Gunther Date: Sat, 20 Aug 2022 23:55:18 -0500 Subject: [PATCH] Code cleanup, only fetch stale blocked servers --- .gitignore | 3 +- src/main.go | 14 ++--- src/redis.go | 35 ++++++++++++- src/status.go | 142 +++++++++++++++++++++++--------------------------- src/util.go | 65 +++++++++++++++++++++-- 5 files changed, 171 insertions(+), 88 deletions(-) diff --git a/.gitignore b/.gitignore index 85cb51e..2fc1f04 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ bin/ config.yml -.DS_Store \ No newline at end of file +.DS_Store +blocked-servers.json \ No newline at end of file diff --git a/src/main.go b/src/main.go index 53d0998..8c777c6 100644 --- a/src/main.go +++ b/src/main.go @@ -24,18 +24,20 @@ func init() { log.Fatal(err) } - if err := r.Connect(config.Redis); err != nil { - log.Fatal(err) - } + r.SetEnabled(config.Cache.Enable) - log.Println("Successfully connected to Redis") + if config.Cache.Enable { + if err := r.Connect(config.Redis); err != nil { + log.Fatal(err) + } + + log.Println("Successfully connected to Redis") + } if err := GetBlockedServerList(); err != nil { log.Fatal(err) } - log.Println("Successfully retrieved EULA blocked servers") - if instanceID := os.Getenv("INSTANCE_ID"); len(instanceID) > 0 { value, err := strconv.ParseUint(instanceID, 10, 16) diff --git a/src/redis.go b/src/redis.go index 1f3db96..a6c3157 100644 --- a/src/redis.go +++ b/src/redis.go @@ -9,10 +9,19 @@ import ( ) type Redis struct { - Client *redis.Client + Enabled bool + Client *redis.Client +} + +func (r *Redis) SetEnabled(value bool) { + r.Enabled = value } func (r *Redis) Connect(uri string) error { + if !r.Enabled { + return nil + } + opts, err := redis.ParseURL(uri) if err != nil { @@ -29,6 +38,10 @@ func (r *Redis) Connect(uri string) error { } func (r *Redis) Exists(key string) (bool, error) { + if !r.Enabled { + return false, nil + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() @@ -45,6 +58,10 @@ func (r *Redis) Exists(key string) (bool, error) { } func (r *Redis) GetString(key string) (string, error) { + if !r.Enabled { + return "", nil + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() @@ -59,6 +76,10 @@ func (r *Redis) GetString(key string) (string, error) { } func (r *Redis) GetBytes(key string) ([]byte, error) { + if !r.Enabled { + return nil, nil + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() @@ -73,6 +94,10 @@ func (r *Redis) GetBytes(key string) ([]byte, error) { } func (r *Redis) Set(key string, value interface{}, ttl time.Duration) error { + if !r.Enabled { + return nil + } + ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) defer cancel() @@ -81,6 +106,10 @@ func (r *Redis) Set(key string, value interface{}, ttl time.Duration) error { } func (r *Redis) SetJSON(key string, value interface{}, ttl time.Duration) error { + if !r.Enabled { + return nil + } + data, err := json.Marshal(value) if err != nil { @@ -91,5 +120,9 @@ func (r *Redis) SetJSON(key string, value interface{}, ttl time.Duration) error } func (r *Redis) Close() error { + if !r.Enabled { + return nil + } + return r.Client.Close() } diff --git a/src/status.go b/src/status.go index 4246b8c..f91b002 100644 --- a/src/status.go +++ b/src/status.go @@ -8,7 +8,7 @@ import ( "github.com/mcstatus-io/mcutil" ) -type StatusOffline struct { +type StatusResponse struct { Online bool `json:"online"` Host string `json:"host"` Port uint16 `json:"port"` @@ -16,28 +16,22 @@ type StatusOffline struct { } type JavaStatusResponse struct { - Online bool `json:"online"` - Host string `json:"host"` - Port uint16 `json:"port"` - EULABlocked bool `json:"eula_blocked"` - Version *JavaVersion `json:"version"` - Players JavaPlayers `json:"players"` - MOTD MOTD `json:"motd"` - Icon *string `json:"icon"` - Mods []Mod `json:"mods"` + StatusResponse + Version *JavaVersion `json:"version"` + Players JavaPlayers `json:"players"` + MOTD MOTD `json:"motd"` + Icon *string `json:"icon"` + Mods []Mod `json:"mods"` } type BedrockStatusResponse struct { - Online bool `json:"online"` - Host string `json:"host"` - Port uint16 `json:"port"` - EULABlocked bool `json:"eula_blocked"` - Version *BedrockVersion `json:"version"` - Players *BedrockPlayers `json:"players"` - MOTD *MOTD `json:"motd"` - Gamemode *string `json:"gamemode"` - ServerID *string `json:"server_id"` - Edition *string `json:"edition"` + StatusResponse + Version *BedrockVersion `json:"version"` + Players *BedrockPlayers `json:"players"` + MOTD *MOTD `json:"motd"` + Gamemode *string `json:"gamemode"` + ServerID *string `json:"server_id"` + Edition *string `json:"edition"` } type JavaVersion struct { @@ -84,24 +78,20 @@ type Mod struct { func GetJavaStatus(host string, port uint16) (interface{}, error) { cacheKey := fmt.Sprintf("java:%s-%d", host, port) - if config.Cache.Enable { - exists, err := r.Exists(cacheKey) + cacheExists, err := r.Exists(cacheKey) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - if exists { - return r.GetString(cacheKey) - } + if cacheExists { + return r.GetString(cacheKey) } response := FetchJavaStatus(host, port) - if config.Cache.Enable { - if err := r.SetJSON(cacheKey, response, config.Cache.JavaCacheDuration); err != nil { - return nil, err - } + if err := r.SetJSON(cacheKey, response, config.Cache.JavaCacheDuration); err != nil { + return nil, err } return response, nil @@ -110,24 +100,20 @@ func GetJavaStatus(host string, port uint16) (interface{}, error) { func GetBedrockStatus(host string, port uint16) (interface{}, error) { cacheKey := fmt.Sprintf("bedrock:%s-%d", host, port) - if config.Cache.Enable { - exists, err := r.Exists(cacheKey) + cacheExists, err := r.Exists(cacheKey) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - if exists { - return r.GetString(cacheKey) - } + if cacheExists { + return r.GetString(cacheKey) } response := FetchBedrockStatus(host, port) - if config.Cache.Enable { - if err := r.SetJSON(cacheKey, response, config.Cache.BedrockCacheDuration); err != nil { - return nil, err - } + if err := r.SetJSON(cacheKey, response, config.Cache.BedrockCacheDuration); err != nil { + return nil, err } return response, nil @@ -136,16 +122,14 @@ func GetBedrockStatus(host string, port uint16) (interface{}, error) { func GetServerIcon(host string, port uint16) ([]byte, error) { cacheKey := fmt.Sprintf("icon:%s-%d", host, port) - if config.Cache.Enable { - exists, err := r.Exists(cacheKey) + cacheExists, err := r.Exists(cacheKey) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - if exists { - return r.GetBytes(cacheKey) - } + if cacheExists { + return r.GetBytes(cacheKey) } icon := defaultIconBytes @@ -162,10 +146,8 @@ func GetServerIcon(host string, port uint16) ([]byte, error) { icon = data } - if config.Cache.Enable { - if err := r.Set(cacheKey, icon, config.Cache.IconCacheDuration); err != nil { - return nil, err - } + if err := r.Set(cacheKey, icon, config.Cache.IconCacheDuration); err != nil { + return nil, err } return icon, nil @@ -178,7 +160,7 @@ func FetchJavaStatus(host string, port uint16) interface{} { statusLegacy, err := mcutil.StatusLegacy(host, port) if err != nil { - return StatusOffline{ + return StatusResponse{ Online: false, Host: host, Port: port, @@ -187,11 +169,13 @@ func FetchJavaStatus(host string, port uint16) interface{} { } response := JavaStatusResponse{ - Online: true, - Host: host, - Port: port, - EULABlocked: IsBlockedAddress(host), - Version: nil, + StatusResponse: StatusResponse{ + Online: true, + Host: host, + Port: port, + EULABlocked: IsBlockedAddress(host), + }, + Version: nil, Players: JavaPlayers{ Online: statusLegacy.Players.Online, Max: statusLegacy.Players.Max, @@ -243,10 +227,12 @@ func FetchJavaStatus(host string, port uint16) interface{} { } return JavaStatusResponse{ - Online: true, - Host: host, - Port: port, - EULABlocked: IsBlockedAddress(host), + StatusResponse: StatusResponse{ + Online: true, + Host: host, + Port: port, + EULABlocked: IsBlockedAddress(host), + }, Version: &JavaVersion{ NameRaw: status.Version.NameRaw, NameClean: status.Version.NameClean, @@ -272,7 +258,7 @@ func FetchBedrockStatus(host string, port uint16) interface{} { status, err := mcutil.StatusBedrock(host, port) if err != nil { - return StatusOffline{ + return StatusResponse{ Online: false, Host: host, Port: port, @@ -281,16 +267,18 @@ func FetchBedrockStatus(host string, port uint16) interface{} { } response := BedrockStatusResponse{ - Online: true, - Host: host, - Port: port, - EULABlocked: IsBlockedAddress(host), - Version: nil, - Players: nil, - MOTD: nil, - Gamemode: status.Gamemode, - ServerID: status.ServerID, - Edition: status.Edition, + StatusResponse: StatusResponse{ + Online: true, + Host: host, + Port: port, + EULABlocked: IsBlockedAddress(host), + }, + Version: nil, + Players: nil, + MOTD: nil, + Gamemode: status.Gamemode, + ServerID: status.ServerID, + Edition: status.Edition, } if status.Version != nil { diff --git a/src/util.go b/src/util.go index 67c51c7..8b03a0f 100644 --- a/src/util.go +++ b/src/util.go @@ -4,20 +4,29 @@ import ( "crypto/sha1" _ "embed" "encoding/hex" + "encoding/json" "fmt" "io/ioutil" + "log" "net/http" + "os" "regexp" "strconv" "strings" + "time" ) var ( //go:embed icon.png defaultIconBytes []byte - ipAddressRegExp = regexp.MustCompile("^\\d{1,3}(\\.\\d{1,3}){3}$") + ipAddressRegExp = regexp.MustCompile(`^\d{1,3}(\.\d{1,3}){3}$`) ) +type BlockedServersFile struct { + LastUpdated time.Time `json:"last_updated"` + Servers []string `json:"servers"` +} + func Contains[T comparable](arr []T, v T) bool { for _, value := range arr { if v == value { @@ -29,6 +38,36 @@ func Contains[T comparable](arr []T, v T) bool { } func GetBlockedServerList() error { + f, err := os.OpenFile("blocked-servers.json", os.O_CREATE|os.O_RDWR, 0777) + + if err != nil { + return err + } + + defer f.Close() + + data, err := ioutil.ReadAll(f) + + if err != nil { + return err + } + + if len(data) > 0 { + var blockedServersFile BlockedServersFile + + if err = json.Unmarshal(data, &blockedServersFile); err != nil { + return err + } + + if time.Since(blockedServersFile.LastUpdated).Hours() < 24 { + blockedServersMutex.Lock() + blockedServers = blockedServersFile.Servers + blockedServersMutex.Unlock() + + return nil + } + } + resp, err := http.Get("https://sessionserver.mojang.com/blockedservers") if err != nil { @@ -39,6 +78,8 @@ func GetBlockedServerList() error { return fmt.Errorf("unexpected status code: %d", resp.StatusCode) } + log.Println("Successfully retrieved EULA blocked servers") + defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) @@ -49,9 +90,27 @@ func GetBlockedServerList() error { blockedServersMutex.Lock() blockedServers = strings.Split(string(body), "\n") - blockedServersMutex.Unlock() - return nil + defer blockedServersMutex.Unlock() + + if data, err = json.Marshal(BlockedServersFile{ + LastUpdated: time.Now(), + Servers: blockedServers, + }); err != nil { + return err + } + + if err = f.Truncate(0); err != nil { + return err + } + + if _, err = f.Seek(0, 0); err != nil { + return err + } + + _, err = f.Write(data) + + return err } func IsBlockedAddress(address string) bool {