diff --git a/plugins/inputs/minecraft/client.go b/plugins/inputs/minecraft/client.go index 166bd8567bab6..fdab824178cce 100644 --- a/plugins/inputs/minecraft/client.go +++ b/plugins/inputs/minecraft/client.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/influxdata/telegraf/plugins/inputs/minecraft/internal/rcon" + "github.com/gorcon/rcon" ) var ( @@ -40,22 +40,12 @@ type connector struct { } func (c *connector) Connect() (Connection, error) { - p, err := strconv.Atoi(c.port) + client, err := rcon.Dial(c.hostname+":"+c.port, c.password) if err != nil { return nil, err } - client, err := rcon.NewClient(c.hostname, p) - if err != nil { - return nil, err - } - - _, err = client.Authorize(c.password) - if err != nil { - return nil, err - } - - return &connection{client: client}, nil + return client, nil } func newClient(connector Connector) *client { @@ -110,18 +100,6 @@ func (c *client) Scores(player string) ([]Score, error) { return parseScores(resp), nil } -type connection struct { - client *rcon.Client -} - -func (c *connection) Execute(command string) (string, error) { - packet, err := c.client.Execute(command) - if err != nil { - return "", err - } - return packet.Body, nil -} - func parsePlayers(input string) []string { parts := strings.SplitAfterN(input, ":", 2) if len(parts) != 2 { diff --git a/plugins/inputs/minecraft/internal/rcon/rcon.go b/plugins/inputs/minecraft/internal/rcon/rcon.go deleted file mode 100644 index e75b0b27670cf..0000000000000 --- a/plugins/inputs/minecraft/internal/rcon/rcon.go +++ /dev/null @@ -1,210 +0,0 @@ -// Package rcon implements the communication protocol for communicating -// with RCON servers. Tested and working with Valve game servers. -package rcon - -import ( - "bytes" - "crypto/rand" - "encoding/binary" - "errors" - "fmt" - "net" - "strings" -) - -const ( - PacketPaddingSize uint8 = 2 // Size of Packet's padding. - PacketHeaderSize uint8 = 8 // Size of Packet's header. -) - -const ( - TerminationSequence = "\x00" // Null empty ASCII string suffix. -) - -// Packet type constants. -// https://developer.valvesoftware.com/wiki/Source_RCON_Protocol#Packet_Type -const ( - Exec int32 = 2 - Auth int32 = 3 - AuthResponse int32 = 2 - ResponseValue int32 = 0 -) - -// Rcon package errors. -var ( - ErrInvalidWrite = errors.New("failed to write the payload correctly to remote connection") - ErrInvalidRead = errors.New("failed to read the response correctly from remote connection") - ErrInvalidChallenge = errors.New("server failed to mirror request challenge") - ErrUnauthorizedRequest = errors.New("client not authorized to remote server") - ErrFailedAuthorization = errors.New("failed to authorize to the remote server") -) - -type Client struct { - Host string // The IP address of the remote server. - Port int // The Port the remote server's listening on. - Authorized bool // Has the client been authorized by the server? - Connection net.Conn // The TCP connection to the server. -} - -type Header struct { - Size int32 // The size of the payload. - Challenge int32 // The challenge ths server should mirror. - Type int32 // The type of request being sent. -} - -type Packet struct { - Header Header // Packet header. - Body string // Body of packet. -} - -// Compile converts a packets header and body into its appropriate -// byte array payload, returning an error if the binary packages -// Write method fails to write the header bytes in their little -// endian byte order. -func (p Packet) Compile() (payload []byte, err error) { - var size = p.Header.Size - var buffer bytes.Buffer - var padding [PacketPaddingSize]byte - - if err = binary.Write(&buffer, binary.LittleEndian, &size); nil != err { - return nil, err - } else if err = binary.Write(&buffer, binary.LittleEndian, &p.Header.Challenge); nil != err { - return nil, err - } else if err = binary.Write(&buffer, binary.LittleEndian, &p.Header.Type); nil != err { - return nil, err - } - - buffer.WriteString(p.Body) - buffer.Write(padding[:]) - - return buffer.Bytes(), nil -} - -// NewPacket returns a pointer to a new Packet type. -func NewPacket(challenge, typ int32, body string) (packet *Packet) { - size := int32(len([]byte(body)) + int(PacketHeaderSize+PacketPaddingSize)) - return &Packet{Header{size, challenge, typ}, body} -} - -// Authorize calls Send with the appropriate command type and the provided -// password. The response packet is returned if authorization is successful -// or a potential error. -func (c *Client) Authorize(password string) (response *Packet, err error) { - if response, err = c.Send(Auth, password); nil == err { - if response.Header.Type != AuthResponse { - return nil, ErrFailedAuthorization - } - c.Authorized = true - } - - return response, err -} - -// Execute calls Send with the appropriate command type and the provided -// command. The response packet is returned if the command executed successfully -// or a potential error. -func (c *Client) Execute(command string) (response *Packet, err error) { - return c.Send(Exec, command) -} - -// Send accepts the commands type and its string to execute to the clients server, -// creating a packet with a random challenge id for the server to mirror, -// and compiling its payload bytes in the appropriate order. The response is -// decompiled from its bytes into a Packet type for return. An error is returned -// if send fails. -func (c *Client) Send(typ int32, command string) (*Packet, error) { - if typ != Auth && !c.Authorized { - return nil, ErrUnauthorizedRequest - } - - // Create a random challenge for the server to mirror in its response. - var challenge int32 - if err := binary.Read(rand.Reader, binary.LittleEndian, &challenge); nil != err { - return nil, err - } - - // Create the packet from the challenge, typ and command - // and compile it to its byte payload - packet := NewPacket(challenge, typ, command) - payload, err := packet.Compile() - if nil != err { - return nil, err - } - - n, err := c.Connection.Write(payload) - if nil != err { - return nil, err - } - if n != len(payload) { - return nil, ErrInvalidWrite - } - - var header Header - if err := binary.Read(c.Connection, binary.LittleEndian, &header.Size); nil != err { - return nil, err - } - if err := binary.Read(c.Connection, binary.LittleEndian, &header.Challenge); nil != err { - return nil, err - } - if err := binary.Read(c.Connection, binary.LittleEndian, &header.Type); nil != err { - return nil, err - } - - if packet.Header.Type == Auth && header.Type == ResponseValue { - // Discard, empty SERVERDATA_RESPONSE_VALUE from authorization. - if _, err := c.Connection.Read(make([]byte, header.Size-int32(PacketHeaderSize))); nil != err { - return nil, err - } - - // Reread the packet header. - if err := binary.Read(c.Connection, binary.LittleEndian, &header.Size); nil != err { - return nil, err - } - if err := binary.Read(c.Connection, binary.LittleEndian, &header.Challenge); nil != err { - return nil, err - } - if err := binary.Read(c.Connection, binary.LittleEndian, &header.Type); nil != err { - return nil, err - } - } - - if header.Challenge != packet.Header.Challenge { - return nil, ErrInvalidChallenge - } - - body := make([]byte, header.Size-int32(PacketHeaderSize)) - n, err = c.Connection.Read(body) - for n < len(body) { - var nBytes int - nBytes, err = c.Connection.Read(body[n:]) - if err != nil { - return nil, err - } - n += nBytes - } - - // Shouldn't this be moved up to the first read? - if nil != err { - return nil, err - } - if n != len(body) { - return nil, ErrInvalidRead - } - - response := new(Packet) - response.Header = header - response.Body = strings.TrimRight(string(body), TerminationSequence) - - return response, nil -} - -// NewClient creates a new Client type, creating the connection -// to the server specified by the host and port arguments. If -// the connection fails, an error is returned. -func NewClient(host string, port int) (client *Client, err error) { - client = new(Client) - client.Host = host - client.Port = port - client.Connection, err = net.Dial("tcp", fmt.Sprintf("%v:%v", client.Host, client.Port)) - return client, err -}