yes
This commit is contained in:
+26
-12
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user