Skip to content

Commit

Permalink
Merge pull request #52 from dreamscached/srv-record
Browse files Browse the repository at this point in the history
Implement SRV record lookups
  • Loading branch information
dreamscached authored Feb 12, 2024
2 parents b17394a + 576dd9a commit 14ef38b
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 0 deletions.
25 changes: 25 additions & 0 deletions net.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package minequery

import (
"errors"
"fmt"
"net"
"strings"
Expand Down Expand Up @@ -58,6 +59,30 @@ func (p *Pinger) openUDPConnWithLocalAddr(host string, remotePort int, localAddr
return conn, nil
}

// resolveSRV performs SRV lookup of a Minecraft server hostname.
//
// In case there are no records found, an empty string, a zero port and nil error are returned.
//
// In case when there is more than one record, the hostname and port of the first record with
// the least weight is returned.
func (p *Pinger) resolveSRV(host string) (string, uint16, error) {
_, records, err := net.LookupSRV("minecraft", "tcp", host)
if err != nil {
var dnsError *net.DNSError
if errors.As(err, &dnsError) && dnsError.IsNotFound {
return "", 0, nil
}

return "", 0, err
}

if len(records) == 0 {
return "", 0, nil
}
target := records[0]
return target.Target, target.Port, nil
}

func shouldWrapIPv6(host string) bool {
return len(host) >= 2 && !(host[0] == '[' && host[1] == ']') && strings.Count(host, ":") >= 2
}
Expand Down
8 changes: 8 additions & 0 deletions ping_14.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ func Ping14(host string, port int) (*Status14, error) {
// Ping14 pings 1.4 to 1.6 (exclusively) Minecraft servers (Notchian servers of more late versions also respond to
// this ping packet.)
func (p *Pinger) Ping14(host string, port int) (*Status14, error) {
status, err := p.pingGeneric(p.ping14, host, port)
if err != nil {
return nil, err
}
return status.(*Status14), nil
}

func (p *Pinger) ping14(host string, port int) (interface{}, error) {
conn, err := p.openTCPConn(host, port)
if err != nil {
return nil, err
Expand Down
8 changes: 8 additions & 0 deletions ping_16.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,14 @@ func Ping16(host string, port int) (*Status16, error) {
// Ping16 pings 1.6 to 1.7 (exclusively) Minecraft servers (Notchian servers of more late versions also respond
// to this ping packet.)
func (p *Pinger) Ping16(host string, port int) (*Status16, error) {
status, err := p.pingGeneric(p.ping16, host, port)
if err != nil {
return nil, err
}
return status.(*Status16), nil
}

func (p *Pinger) ping16(host string, port int) (interface{}, error) {
conn, err := p.openTCPConn(host, port)
if err != nil {
return nil, err
Expand Down
8 changes: 8 additions & 0 deletions ping_17.go
Original file line number Diff line number Diff line change
Expand Up @@ -1745,6 +1745,14 @@ func Ping17(host string, port int) (*Status17, error) {

// Ping17 pings 1.7+ Minecraft servers.
func (p *Pinger) Ping17(host string, port int) (*Status17, error) {
status, err := p.pingGeneric(p.ping17, host, port)
if err != nil {
return nil, err
}
return status.(*Status17), nil
}

func (p *Pinger) ping17(host string, port int) (interface{}, error) {
conn, err := p.openTCPConn(host, port)
if err != nil {
return nil, err
Expand Down
8 changes: 8 additions & 0 deletions ping_beta18.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ func PingBeta18(host string, port int) (*StatusBeta18, error) {
// PingBeta18 pings Beta 1.8 to Release 1.4 (exclusively) Minecraft servers (Notchian servers of more late versions
// also respond to this ping packet.)
func (p *Pinger) PingBeta18(host string, port int) (*StatusBeta18, error) {
status, err := p.pingGeneric(p.pingBeta18, host, port)
if err != nil {
return nil, err
}
return status.(*StatusBeta18), nil
}

func (p *Pinger) pingBeta18(host string, port int) (interface{}, error) {
conn, err := p.openTCPConn(host, port)
if err != nil {
return nil, err
Expand Down
50 changes: 50 additions & 0 deletions ping_generic.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package minequery

// defaultMinecraftPort is a default port Minecraft server runs on and which
// will be used when server port is left as zero value.
const defaultMinecraftPort = 25565

// pingGeneric accepts version-specific ping function and host/port pair. Then it performs
// (if necessary, see PreferSRVRecord) SRV lookup, and attempts to use the SRV record hostname and port
// (first returned record is used if more than one is returned, see net.LookupSRV documentation)
// to ping, if lookup fails or ping fails, the provided hostname/port pair is used directly.
func (p *Pinger) pingGeneric(pingFn func(string, int) (interface{}, error), host string, port int) (interface{}, error) {
// Use default Minecraft port if port is 0
if port == 0 {
port = defaultMinecraftPort
}

if p.PreferSRVRecord {
// When SRV record is preferred, try resolving it
srvHost, srvPort, err := p.resolveSRV(host)
if err != nil {
if p.UseStrict {
// If UseStrict, SRV lookup error is fatal
return nil, err
}

// If not UseStrict, continue pinging on the desired host/port

} else {
// If SRV lookup is successful, check if there are any records
if srvHost != "" {
status, err := pingFn(srvHost, int(srvPort))
if err != nil {
// If pinging on the SRV record failed and UseStrict is set,
// this is fatal enough to raise an error
if p.UseStrict {
return nil, err
}

} else {
// Success, SRV record ping passed
return status, nil
}
}

}
}

// Otherwise just ping normally
return pingFn(host, port)
}
14 changes: 14 additions & 0 deletions pinger.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ func WithUseStrict(useStrict bool) PingerOption {
return func(p *Pinger) { p.UseStrict = useStrict }
}

// WithPreferSRVRecord sets Pinger PreferSRVRecord to the provided value.
//
//goland:noinspection GoUnusedExportedFunction
func WithPreferSRVRecord(preferSRV bool) PingerOption {
return func(p *Pinger) {
p.PreferSRVRecord = preferSRV
}
}

// WithProtocolVersion16 sets Pinger ProtocolVersion16 value.
//
//goland:noinspection GoUnusedExportedFunction
Expand Down Expand Up @@ -121,6 +130,10 @@ type Pinger struct {
// that are by default silently ignored should be actually returned as errors.
UseStrict bool

// PreferSRVRecord is a configuration value that defines if Pinger will prefer SRV records, which is the
// default behavior of Minecraft clients.
PreferSRVRecord bool

// UnmarshalFunc is the function used to unmarshal JSON (used by Ping17 for responses from 1.7+ servers).
// By default, it uses json.Unmarshal function.
UnmarshalFunc UnmarshalFunc
Expand Down Expand Up @@ -151,6 +164,7 @@ func newDefaultPinger() *Pinger {
WithDialer(&net.Dialer{})(p)
WithQueryCacheExpiry(30*time.Second, 5*time.Minute)(p)
WithTimeout(15 * time.Second)(p)
WithPreferSRVRecord(true)(p)
WithUnmarshaller(json.Unmarshal)(p)
WithImageEncoding(base64.StdEncoding)(p)
WithImageDecoder(png.Decode)(p)
Expand Down

0 comments on commit 14ef38b

Please sign in to comment.