This commit is contained in:
adman
2026-07-16 01:13:31 +02:00
parent b966925212
commit 5c5c391aa6
14 changed files with 441 additions and 120 deletions
+26 -12
View File
@@ -4,33 +4,47 @@ import (
"context"
"net"
"github.com/admin-else/strom/mc/data"
"github.com/admin-else/strom/mc/proto"
"github.com/admin-else/strom/mc/proto_base"
"github.com/admin-else/strom/mc/server"
)
// Connect connects to the given address note that it does not resolve the SRV records it's just a raw net.Dial
// You must close the connection when you're done with it
func Connect(addr string) (ret *proto.Conn, err error) {
s, err := Status(addr)
// version must be a vaild minecraft version eg 1.21.11 if left empty it will be detected with a status-request.
func Connect(ctx context.Context, addr string, version string) (ret *proto.Conn, err error) {
var protocolVersion int32
if version == "" {
var s server.StatusResponse
s, err = Status(ctx, addr)
if err != nil {
return
}
protocolVersion = s.Version.Protocol
} else {
var v data.VersionInfo
v, err = data.LookUpVersionByName(version)
if err != nil {
return
}
protocolVersion = v.Version
}
ret, err = ConnectVersionLess(ctx, addr)
if err != nil {
return
}
ret, err = ConnectVersionLess(addr)
if err != nil {
return
}
err = ret.SetProtocolVersion(s.Version.Protocol)
err = ret.SetProtocolVersion(protocolVersion)
return
}
func ConnectVersionLess(addr string) (ret *proto.Conn, err error) {
return ConnectVersionLessCtx(context.Background(), addr)
}
// ConnectVersionLess connects to the given address without setting the version you should set it
// when using this function.
// You must close the connection when you're done with it
func ConnectVersionLessCtx(ctx context.Context, addr string) (ret *proto.Conn, err error) {
func ConnectVersionLess(ctx context.Context, addr string) (ret *proto.Conn, err error) {
if ctx == nil {
ctx = context.Background()
}
ret = proto.NewConnCtx(ctx)
ret.Actor = proto_base.Client
ret.Conn, err = (&net.Dialer{}).DialContext(ctx, "tcp", addr)
+10 -56
View File
@@ -11,21 +11,21 @@ import (
type DNSCheckFunc func(addr string) error
// DoDns resolves a DNS name to an IP address
// It should resolve a minecraft compatible address into something net.Dial
func DoDns(in string) (out string, host string, portN uint16, err error) {
return DoDNSChecked(in, nil)
}
// DoDNSChecked resolves a DNS name to an IP address
// It should resolve a minecraft compatible address into something net.Dial
// It also optionally accepts a function to "check" the address
func DoDNSChecked(in string, f DNSCheckFunc) (connectTo string, host string, portN uint16, err error) {
func DoDNSChecked(ctx context.Context, in string, f DNSCheckFunc) (connectTo string, host string, portN uint16, err error) {
if f == nil {
f = func(addr string) error { return nil }
}
if ctx == nil {
ctx = context.Background()
}
// https://mcsrc.dev/1/26.1-pre-1/net/minecraft/client/multiplayer/resolver/ServerAddress#L34
host, port, err := net.SplitHostPort(in)
if err != nil {
var addrErr *net.AddrError
if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" {
if addrErr, ok := errors.AsType[*net.AddrError](err); ok && addrErr.Err == "missing port in address" {
err = nil
host = in
port = "25565"
@@ -37,9 +37,6 @@ func DoDNSChecked(in string, f DNSCheckFunc) (connectTo string, host string, por
if err != nil {
return
}
if f == nil {
f = func(addr string) error { return nil }
}
err = f(host)
if err != nil {
@@ -58,7 +55,7 @@ func DoDNSChecked(in string, f DNSCheckFunc) (connectTo string, host string, por
if portN != 25565 {
return
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second) // otherwise this takes 10+ seconds
ctx, cancel := context.WithTimeout(ctx, time.Second) // otherwise this takes 10+ seconds
defer cancel()
r := &net.Resolver{}
@@ -81,46 +78,3 @@ func DoDNSChecked(in string, f DNSCheckFunc) (connectTo string, host string, por
connectTo = outIP.String() + ":" + port
return
}
// DoDnsSimple resolves a DNS name to an IP address
// It should resolve a minecraft compatible address into something net.Dial
// It does not check the address
func DoDnsSimple(in string) (out string, err error) {
host, port, err := net.SplitHostPort(in)
if err != nil {
var addrErr *net.AddrError
if errors.As(err, &addrErr) && addrErr.Err == "missing port in address" {
err = nil
host = in
port = "25565"
} else {
return
}
}
porti, err := strconv.Atoi(port)
if err != nil {
return
}
portN := uint16(porti)
_, err = net.ResolveIPAddr("ip", host)
if err != nil {
return
}
out = host + ":" + strconv.Itoa(int(portN))
if portN != 25565 {
return
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second) // otherwise this takes 10+ seconds
defer cancel()
r := &net.Resolver{}
_, srvs, _ := r.LookupSRV(ctx, "minecraft", "tcp", host)
if len(srvs) == 0 {
return
}
host = strings.TrimSuffix(srvs[0].Target, ".")
portN = srvs[0].Port
out = host + ":" + strconv.Itoa(int(portN))
return
}
+1 -1
View File
@@ -15,7 +15,7 @@ func TestDoDNS(t *testing.T) {
}
for _, c := range cases {
t.Logf("Testing %s", c.Input)
_, host, portN, err := DoDns(c.Input)
_, host, portN, err := DoDNSChecked(nil, c.Input, nil)
if err != nil {
t.Error(err)
continue
+43 -11
View File
@@ -1,6 +1,7 @@
package client
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
@@ -155,24 +156,55 @@ func LoginRaw(c *proto.Conn, account *api.Account) (err error) {
return
}
func LoginNoSrv(connectTo string, account *api.Account) (c *proto.Conn, err error) {
c, err = Connect(connectTo)
if err != nil {
return
func WithoutDns() func(ls *loginSettings) {
return func(ls *loginSettings) {
ls.NoDns = true
}
err = LoginRaw(c, account)
return
}
func Login(connectTo string, account *api.Account) (c *proto.Conn, err error) {
connectTo, err = DoDnsSimple(connectTo)
if err != nil {
return
func WithContext(ctx context.Context) func(ls *loginSettings) {
return func(ls *loginSettings) {
ls.Ctx = ctx
}
c, err = Connect(connectTo)
}
func WithVersion(version string) func(ls *loginSettings) {
return func(ls *loginSettings) {
ls.Version = version
}
}
type loginSettings struct {
NoDns bool
Ctx context.Context
Version string
IgnoreConfig bool
}
func Login(connectTo string, account *api.Account, settings ...func(ls *loginSettings)) (c *proto.Conn, err error) {
ls := &loginSettings{}
for _, setting := range settings {
setting(ls)
}
if !ls.NoDns {
connectTo, _, _, err = DoDNSChecked(ls.Ctx, connectTo, nil)
if err != nil {
return
}
}
c, err = Connect(ls.Ctx, connectTo, ls.Version)
if err != nil {
return
}
err = LoginRaw(c, account)
if err != nil {
return
}
if !ls.IgnoreConfig {
err = IgnoreConfig(c)
}
return
}
+3 -3
View File
@@ -40,7 +40,7 @@ func (s *StatusClient) OnPong(p *v1_21_8.StatusToClientPacketPing) (err error) {
// So ignore the resource leak warning. And maybe attach a warn ignore comment.
// It does not resolve SRV records.
func StatusRaw(ctx context.Context, addr string) (s *StatusClient, err error) {
c, err := ConnectVersionLess(addr)
c, err := ConnectVersionLess(ctx, addr)
if err != nil {
return
}
@@ -81,8 +81,8 @@ func StatusNoDns(addr string) (status server.StatusResponse, err error) {
}
// Status resolves the status of the minecraft server at the given address.
func Status(addr string) (status server.StatusResponse, err error) {
addr, err = DoDnsSimple(addr)
func Status(ctx context.Context, addr string) (status server.StatusResponse, err error) {
addr, _, _, err = DoDNSChecked(ctx, addr, nil)
if err != nil {
return
}