diff --git a/go.mod b/go.mod index 67663b6..d63c65a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.22.3 require ( github.com/go-redsync/redsync/v4 v4.13.0 github.com/gofiber/fiber/v2 v2.52.5 - github.com/mcstatus-io/mcutil/v4 v4.0.0-20240719034021-32d6524c0a5a + github.com/mcstatus-io/mcutil/v4 v4.0.0-20240810144107-526e8f097db7 github.com/redis/go-redis/v9 v9.5.4 go.mongodb.org/mongo-driver v1.16.0 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 99858f5..a759365 100644 --- a/go.sum +++ b/go.sum @@ -44,6 +44,8 @@ github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZ github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mcstatus-io/mcutil/v4 v4.0.0-20240719034021-32d6524c0a5a h1:BnQPuCQvDabddAqVBjJ0US8TjI8/AKe//KkYGmZ75iM= github.com/mcstatus-io/mcutil/v4 v4.0.0-20240719034021-32d6524c0a5a/go.mod h1:yC91WInI1U2GAMFWgpPgsAULPVS2o+4JCZbiiWhHwxM= +github.com/mcstatus-io/mcutil/v4 v4.0.0-20240810144107-526e8f097db7 h1:DaSQZf8L5ali7HmUDJq/7pf06U2yapEer3caRka5dJs= +github.com/mcstatus-io/mcutil/v4 v4.0.0-20240810144107-526e8f097db7/go.mod h1:yC91WInI1U2GAMFWgpPgsAULPVS2o+4JCZbiiWhHwxM= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= github.com/redis/go-redis/v9 v9.5.4 h1:vOFYDKKVgrI5u++QvnMT7DksSMYg7Aw/Np4vLJLKLwY= diff --git a/src/routes.go b/src/routes.go index a015ea9..45e2a19 100644 --- a/src/routes.go +++ b/src/routes.go @@ -55,15 +55,15 @@ func PingHandler(ctx *fiber.Ctx) error { // JavaStatusHandler returns the status of the Java edition Minecraft server specified in the address parameter. func JavaStatusHandler(ctx *fiber.Ctx) error { - address := strings.ToLower(ctx.Params("address")) - opts, err := GetStatusOptions(ctx) if err != nil { return err } - if _, _, err := util.ParseAddress(address); err != nil { + hostname, port, err := ParseAddress(strings.ToLower(ctx.Params("address")), util.DefaultJavaPort) + + if err != nil { return ctx.Status(http.StatusBadRequest).SendString("Invalid address value") } @@ -75,11 +75,11 @@ func JavaStatusHandler(ctx *fiber.Ctx) error { return err } - if err = r.Increment(fmt.Sprintf("java-hits:%s", address)); err != nil { + if err = r.Increment(fmt.Sprintf("java-hits:%s", fmt.Sprintf("%s:%d", hostname, port))); err != nil { return err } - response, expiresAt, err := GetJavaStatus(address, opts) + response, expiresAt, err := GetJavaStatus(hostname, port, opts) if err != nil { return err @@ -96,23 +96,23 @@ func JavaStatusHandler(ctx *fiber.Ctx) error { // BedrockStatusHandler returns the status of the Bedrock edition Minecraft server specified in the address parameter. func BedrockStatusHandler(ctx *fiber.Ctx) error { - address := strings.ToLower(ctx.Params("address")) - opts, err := GetStatusOptions(ctx) if err != nil { return err } - if _, _, err := util.ParseAddress(address); err != nil { + hostname, port, err := ParseAddress(strings.ToLower(ctx.Params("address")), util.DefaultBedrockPort) + + if err != nil { return ctx.Status(http.StatusBadRequest).SendString("Invalid address value") } - if err = r.Increment(fmt.Sprintf("bedrock-hits:%s", address)); err != nil { + if err = r.Increment(fmt.Sprintf("bedrock-hits:%s", fmt.Sprintf("%s:%d", hostname, port))); err != nil { return err } - response, expiresAt, err := GetBedrockStatus(address, opts) + response, expiresAt, err := GetBedrockStatus(hostname, port, opts) if err != nil { return err @@ -129,19 +129,19 @@ func BedrockStatusHandler(ctx *fiber.Ctx) error { // IconHandler returns the server icon for the specified Java edition Minecraft server. func IconHandler(ctx *fiber.Ctx) error { - address := strings.ToLower(ctx.Params("address")) - opts, err := GetStatusOptions(ctx) if err != nil { return err } - if _, _, err := util.ParseAddress(address); err != nil { + hostname, port, err := ParseAddress(strings.ToLower(ctx.Params("address")), util.DefaultJavaPort) + + if err != nil { return ctx.Status(http.StatusBadRequest).SendString("Invalid address value") } - icon, expiresAt, err := GetServerIcon(address, opts) + icon, expiresAt, err := GetServerIcon(hostname, port, opts) if err != nil { return err diff --git a/src/status.go b/src/status.go index aac1075..71563a2 100644 --- a/src/status.go +++ b/src/status.go @@ -126,8 +126,8 @@ type SRVRecord struct { } // GetJavaStatus returns the status response of a Java Edition server, either using cache or fetching a fresh status. -func GetJavaStatus(host string, opts *StatusOptions) (*JavaStatusResponse, time.Duration, error) { - cacheKey := GetCacheKey(host, opts) +func GetJavaStatus(hostname string, port uint16, opts *StatusOptions) (*JavaStatusResponse, time.Duration, error) { + cacheKey := GetCacheKey(hostname, port, opts) // Wait for any other processes to finish fetching the status of this server if config.Cache.EnableLocks { @@ -156,7 +156,7 @@ func GetJavaStatus(host string, opts *StatusOptions) (*JavaStatusResponse, time. // Fetch a fresh status from the server itself { - response, err := FetchJavaStatus(host, opts) + response, err := FetchJavaStatus(hostname, port, opts) if err != nil { return nil, 0, err @@ -177,8 +177,8 @@ func GetJavaStatus(host string, opts *StatusOptions) (*JavaStatusResponse, time. } // GetBedrockStatus returns the status response of a Bedrock Edition server, either using cache or fetching a fresh status. -func GetBedrockStatus(host string, opts *StatusOptions) (*BedrockStatusResponse, time.Duration, error) { - cacheKey := GetCacheKey(host, nil) +func GetBedrockStatus(hostname string, port uint16, opts *StatusOptions) (*BedrockStatusResponse, time.Duration, error) { + cacheKey := GetCacheKey(hostname, port, nil) // Wait for any other processes to finish fetching the status of this server if config.Cache.EnableLocks { @@ -207,7 +207,7 @@ func GetBedrockStatus(host string, opts *StatusOptions) (*BedrockStatusResponse, // Fetch a fresh status from the server itself { - response, err := FetchBedrockStatus(host, opts) + response, err := FetchBedrockStatus(hostname, port, opts) if err != nil { return nil, 0, err @@ -228,8 +228,8 @@ func GetBedrockStatus(host string, opts *StatusOptions) (*BedrockStatusResponse, } // GetServerIcon returns the icon image of a Java Edition server, either using cache or fetching a fresh image. -func GetServerIcon(host string, opts *StatusOptions) ([]byte, time.Duration, error) { - cacheKey := GetCacheKey(host, nil) +func GetServerIcon(hostname string, port uint16, opts *StatusOptions) ([]byte, time.Duration, error) { + cacheKey := GetCacheKey(hostname, port, nil) // Fetch the cached icon if it exists { @@ -254,7 +254,7 @@ func GetServerIcon(host string, opts *StatusOptions) ([]byte, time.Duration, err defer cancel() - status, err := status.Modern(ctx, host) + status, err := status.Modern(ctx, hostname, port) if err == nil && status.Favicon != nil && strings.HasPrefix(*status.Favicon, "data:image/png;base64,") { data, err := base64.StdEncoding.DecodeString(strings.TrimPrefix(*status.Favicon, "data:image/png;base64,")) @@ -278,11 +278,11 @@ func GetServerIcon(host string, opts *StatusOptions) ([]byte, time.Duration, err } // FetchJavaStatus fetches fresh information about a Java Edition Minecraft server. -func FetchJavaStatus(host string, opts *StatusOptions) (*JavaStatusResponse, error) { +func FetchJavaStatus(hostname string, port uint16, opts *StatusOptions) (*JavaStatusResponse, error) { var ( err error srvRecord *net.SRV - resolvedHost string = host + resolvedHostname string = hostname ipAddress *string statusResult *response.StatusModern legacyStatusResult *response.StatusLegacy @@ -301,16 +301,16 @@ func FetchJavaStatus(host string, opts *StatusOptions) (*JavaStatusResponse, err // Lookup the SRV record { - srvRecord, err = util.LookupSRV(host) + srvRecord, err = util.LookupSRV(hostname) if err == nil && srvRecord != nil { - resolvedHost = strings.Trim(srvRecord.Target, ".") + resolvedHostname = strings.Trim(srvRecord.Target, ".") } } // Resolve the connection hostname to an IP address { - addr, err := net.ResolveIPAddr("ip", resolvedHost) + addr, err := net.ResolveIPAddr("ip", resolvedHostname) if err == nil && addr != nil { ipAddress = PointerOf(addr.IP.String()) @@ -328,11 +328,11 @@ func FetchJavaStatus(host string, opts *StatusOptions) (*JavaStatusResponse, err // Retrieve the post-netty rewrite Java Edition status (Minecraft 1.8+) { go func() { - statusResult, _ = status.Modern(statusContext, host, options.StatusModern{ + statusResult, _ = status.Modern(statusContext, hostname, port, options.StatusModern{ EnableSRV: true, Timeout: opts.Timeout - time.Millisecond*100, - ProtocolVersion: 47, - Ping: false, + ProtocolVersion: -1, + Ping: true, }) wg.Done() @@ -352,10 +352,10 @@ func FetchJavaStatus(host string, opts *StatusOptions) (*JavaStatusResponse, err // Retrieve the pre-netty rewrite Java Edition status (Minecraft 1.7 and below) { go func() { - legacyStatusResult, _ = status.Legacy(legacyContext, host, options.StatusLegacy{ + legacyStatusResult, _ = status.Legacy(legacyContext, hostname, port, options.StatusLegacy{ EnableSRV: true, Timeout: opts.Timeout - time.Millisecond*100, - ProtocolVersion: 47, + ProtocolVersion: -1, }) wg.Done() @@ -371,7 +371,7 @@ func FetchJavaStatus(host string, opts *StatusOptions) (*JavaStatusResponse, err // Retrieve the query information (if it is available) if opts.Query { go func() { - queryResult, _ = query.Full(queryContext, host, options.Query{ + queryResult, _ = query.Full(queryContext, hostname, port, options.Query{ Timeout: opts.Timeout - time.Millisecond*100, }) @@ -381,11 +381,11 @@ func FetchJavaStatus(host string, opts *StatusOptions) (*JavaStatusResponse, err wg.Wait() - return BuildJavaResponse(host, statusResult, legacyStatusResult, queryResult, srvRecord, ipAddress) + return BuildJavaResponse(hostname, port, statusResult, legacyStatusResult, queryResult, srvRecord, ipAddress) } // FetchBedrockStatus fetches a fresh status of a Bedrock Edition server. -func FetchBedrockStatus(host string, opts *StatusOptions) (*BedrockStatusResponse, error) { +func FetchBedrockStatus(hostname string, port uint16, opts *StatusOptions) (*BedrockStatusResponse, error) { var ( ipAddress *string result *response.StatusBedrock @@ -393,7 +393,7 @@ func FetchBedrockStatus(host string, opts *StatusOptions) (*BedrockStatusRespons // Resolve the connection hostname to an IP address { - ipAddr, err := net.ResolveIPAddr("ip", host) + ipAddr, err := net.ResolveIPAddr("ip", hostname) if err == nil && ipAddr != nil { ipAddress = PointerOf(ipAddr.IP.String()) @@ -406,31 +406,21 @@ func FetchBedrockStatus(host string, opts *StatusOptions) (*BedrockStatusRespons defer cancel() - result, _ = status.Bedrock(ctx, host) + result, _ = status.Bedrock(ctx, hostname, port) } - return BuildBedrockResponse(host, result, ipAddress) + return BuildBedrockResponse(hostname, port, result, ipAddress) } // BuildJavaResponse builds the response data from the status and query information. -func BuildJavaResponse(host string, status *response.StatusModern, legacyStatus *response.StatusLegacy, query *response.QueryFull, srvRecord *net.SRV, ipAddress *string) (result *JavaStatusResponse, err error) { - hostname, port, err := util.ParseAddress(host) - - if err != nil { - return nil, err - } - - if port == nil { - port = PointerOf(uint16(util.DefaultJavaPort)) - } - +func BuildJavaResponse(hostname string, port uint16, status *response.StatusModern, legacyStatus *response.StatusLegacy, query *response.QueryFull, srvRecord *net.SRV, ipAddress *string) (result *JavaStatusResponse, err error) { result = &JavaStatusResponse{ BaseStatus: BaseStatus{ Online: false, Host: hostname, - Port: *port, + Port: port, IPAddress: ipAddress, - EULABlocked: IsBlockedAddress(host), + EULABlocked: IsBlockedAddress(hostname), RetrievedAt: time.Now().UnixMilli(), ExpiresAt: time.Now().Add(config.Cache.JavaStatusDuration).UnixMilli(), }, @@ -620,24 +610,14 @@ func BuildJavaResponse(host string, status *response.StatusModern, legacyStatus } // BuildBedrockResponse builds the response data from the status information. -func BuildBedrockResponse(host string, status *response.StatusBedrock, ipAddress *string) (result *BedrockStatusResponse, err error) { - hostname, port, err := util.ParseAddress(host) - - if err != nil { - return nil, err - } - - if port == nil { - port = PointerOf(uint16(util.DefaultBedrockPort)) - } - +func BuildBedrockResponse(hostname string, port uint16, status *response.StatusBedrock, ipAddress *string) (result *BedrockStatusResponse, err error) { result = &BedrockStatusResponse{ BaseStatus: BaseStatus{ Online: false, Host: hostname, - Port: *port, + Port: port, IPAddress: ipAddress, - EULABlocked: IsBlockedAddress(host), + EULABlocked: IsBlockedAddress(hostname), RetrievedAt: time.Now().UnixMilli(), ExpiresAt: time.Now().Add(config.Cache.BedrockStatusDuration).UnixMilli(), }, diff --git a/src/util.go b/src/util.go index 9b5774d..c35586a 100644 --- a/src/util.go +++ b/src/util.go @@ -270,9 +270,10 @@ func GetInstanceID() (uint16, error) { } // GetCacheKey generates a unique key used for caching status results in Redis. -func GetCacheKey(host string, opts *StatusOptions) string { +func GetCacheKey(hostname string, port uint16, opts *StatusOptions) string { values := &url.Values{} - values.Set("host", host) + values.Set("hostname", hostname) + values.Set("port", strconv.FormatUint(uint64(port), 10)) if opts != nil { values.Set("query", strconv.FormatBool(opts.Query))