proxy using support. close #231

This commit is contained in:
Tnze
2023-02-15 19:32:59 +08:00
parent 632df9138c
commit 18ba5e6a30
2 changed files with 37 additions and 19 deletions

View File

@ -10,12 +10,13 @@ import (
"net" "net"
"strconv" "strconv"
"github.com/google/uuid"
"github.com/Tnze/go-mc/chat" "github.com/Tnze/go-mc/chat"
"github.com/Tnze/go-mc/data/packetid" "github.com/Tnze/go-mc/data/packetid"
mcnet "github.com/Tnze/go-mc/net" mcnet "github.com/Tnze/go-mc/net"
pk "github.com/Tnze/go-mc/net/packet" pk "github.com/Tnze/go-mc/net/packet"
"github.com/Tnze/go-mc/yggdrasil/user" "github.com/Tnze/go-mc/yggdrasil/user"
"github.com/google/uuid"
) )
// ProtocolVersion is the protocol version number of minecraft net protocol // ProtocolVersion is the protocol version number of minecraft net protocol
@ -25,7 +26,7 @@ const (
) )
type JoinOptions struct { type JoinOptions struct {
Dialer *net.Dialer MCDialer mcnet.MCDialer
Context context.Context Context context.Context
// Indicate not to fetch and sending player's PubKey // Indicate not to fetch and sending player's PubKey
@ -41,21 +42,21 @@ type JoinOptions struct {
func (c *Client) JoinServer(addr string) (err error) { func (c *Client) JoinServer(addr string) (err error) {
return c.join(addr, JoinOptions{ return c.join(addr, JoinOptions{
Context: context.Background(), Context: context.Background(),
Dialer: (*net.Dialer)(&mcnet.DefaultDialer), MCDialer: &mcnet.DefaultDialer,
}) })
} }
// JoinServerWithDialer is similar to JoinServer but using a Dialer. // JoinServerWithDialer is similar to JoinServer but using a net.Dialer.
func (c *Client) JoinServerWithDialer(dialer *net.Dialer, addr string) (err error) { func (c *Client) JoinServerWithDialer(dialer *net.Dialer, addr string) (err error) {
return c.join(addr, JoinOptions{ return c.join(addr, JoinOptions{
Context: context.Background(), Context: context.Background(),
Dialer: dialer, MCDialer: (*mcnet.Dialer)(dialer),
}) })
} }
func (c *Client) JoinServerWithOptions(addr string, options JoinOptions) (err error) { func (c *Client) JoinServerWithOptions(addr string, options JoinOptions) (err error) {
if options.Dialer == nil { if options.MCDialer == nil {
options.Dialer = (*net.Dialer)(&mcnet.DefaultDialer) options.MCDialer = &mcnet.DefaultDialer
} }
if options.Context == nil { if options.Context == nil {
options.Context = context.Background() options.Context = context.Background()
@ -65,7 +66,9 @@ func (c *Client) JoinServerWithOptions(addr string, options JoinOptions) (err er
func (c *Client) join(addr string, options JoinOptions) error { func (c *Client) join(addr string, options JoinOptions) error {
const Handshake = 0x00 const Handshake = 0x00
// Split Host and Port
// Split Host and Port. The DialMCContext will do this once,
// but we need the result for sending handshake packet here.
host, portStr, err := net.SplitHostPort(addr) host, portStr, err := net.SplitHostPort(addr)
var port uint64 var port uint64
if err != nil { if err != nil {
@ -85,9 +88,7 @@ func (c *Client) join(addr string, options JoinOptions) error {
} }
// Dial connection // Dial connection
d := (*mcnet.Dialer)(options.Dialer) c.Conn, err = options.MCDialer.DialMCContext(options.Context, addr)
ctx := options.Context
c.Conn, err = d.DialMCContext(ctx, addr)
if err != nil { if err != nil {
return LoginErr{"connect server", err} return LoginErr{"connect server", err}
} }

View File

@ -62,6 +62,23 @@ func DialMCTimeout(addr string, timeout time.Duration) (*Conn, error) {
return DefaultDialer.DialMCContext(ctx, addr) return DefaultDialer.DialMCContext(ctx, addr)
} }
// MCDialer provide DialMCContext method, can be used to dial a minecraft server.
// [Dialer] is its default implementation, and support SRV lookup.
//
// Typically, if you want to use built-in proxies or custom dialer,
// you can hook go-mc/bot package by implement this interface.
// When implementing a custom MCDialer, SRV lookup is optional.
type MCDialer interface {
// The DialMCContext dial TCP connection to a minecraft server, and warp the net.Conn by calling [WrapConn].
DialMCContext(ctx context.Context, addr string) (*Conn, error)
}
// Dialer implements MCDialer interface.
//
// It can be easily convert from net.Dialer.
//
// dialer := net.Dialer{}
// mcDialer := (*Dialer)(&dialer)
type Dialer net.Dialer type Dialer net.Dialer
func (d *Dialer) resolver() *net.Resolver { func (d *Dialer) resolver() *net.Resolver {
@ -82,23 +99,23 @@ func (d *Dialer) DialMCContext(ctx context.Context, addr string) (*Conn, error)
return nil, err return nil, err
} }
} }
var ras []string var addresses []string
if port == "" { if port == "" {
// We look up SRV only if the port is not specified // We look up SRV only if the port is not specified
_, srvRecords, err := d.resolver().LookupSRV(ctx, "minecraft", "tcp", host) _, srvRecords, err := d.resolver().LookupSRV(ctx, "minecraft", "tcp", host)
if err == nil { if err == nil {
for _, record := range srvRecords { for _, record := range srvRecords {
addr := net.JoinHostPort(record.Target, strconv.Itoa(int(record.Port))) addr := net.JoinHostPort(record.Target, strconv.Itoa(int(record.Port)))
ras = append(ras, addr) addresses = append(addresses, addr)
} }
} }
// Whatever the SRV records is found, // Whatever the SRV records is found,
addr = net.JoinHostPort(addr, strconv.Itoa(DefaultPort)) addr = net.JoinHostPort(addr, strconv.Itoa(DefaultPort))
} }
ras = append(ras, addr) addresses = append(addresses, addr)
var firstErr error var firstErr error
for i, addr := range ras { for i, addr := range addresses {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, context.Canceled return nil, context.Canceled
@ -106,7 +123,7 @@ func (d *Dialer) DialMCContext(ctx context.Context, addr string) (*Conn, error)
} }
dialCtx := ctx dialCtx := ctx
if deadline, hasDeadline := ctx.Deadline(); hasDeadline { if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
partialDeadline, err := partialDeadline(time.Now(), deadline, len(ras)-i) partialDeadline, err := partialDeadline(time.Now(), deadline, len(addresses)-i)
if err != nil { if err != nil {
// Ran out of time. // Ran out of time.
if firstErr == nil { if firstErr == nil {