Initial commit.

This commit is contained in:
Michael Bang 2015-04-11 15:40:41 +02:00
commit 396c9d5b16
4 changed files with 200 additions and 0 deletions

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Michael Bang
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

10
README.md Normal file
View File

@ -0,0 +1,10 @@
This is a very simple minecraft (and source engine?) RCON client. See [cli/main.go](cli/main.go) for an example on how to use it.
# Shortcomings
- Long (split) responses aren't handled correctly.
- Probably a lot more.
# License
See the [LICENSE](LICENSE) file.

51
cli/main.go Normal file
View File

@ -0,0 +1,51 @@
package main
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/katnegermis/pocketmine-rcon"
)
func main() {
if len(os.Args) < 2 {
fmt.Printf("Usage: ./rcon address password")
return
}
addr := os.Args[1]
pass := os.Args[2]
conn, err := rcon.NewConnection(addr, pass)
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("Successfully logged in at %s!\n", addr)
prompt()
stdin := bufio.NewReader(os.Stdin)
input := ""
for {
if input, err = stdin.ReadString('\n'); err != nil {
fmt.Println(err)
return
}
input = strings.Trim(input[:len(input)-1], " ")
if input == ".exit" {
break
}
if len(input) == 0 {
prompt()
continue
}
r := conn.SendCommand(input)
fmt.Printf("Server:\n%s\n", r)
prompt()
}
}
func prompt() {
fmt.Print("Enter command:\n>")
}

118
connection.go Normal file
View File

@ -0,0 +1,118 @@
package rcon
import (
"bytes"
"encoding/binary"
"errors"
"log"
"net"
)
type Connection struct {
conn net.Conn
pass string
addr string
}
var uniqueId int32 = 0
func NewConnection(addr, pass string) (*Connection, error) {
conn, err := net.Dial("tcp", addr)
if err != nil {
log.Fatal(err)
}
c := &Connection{conn: conn, pass: pass, addr: addr}
if err := c.auth(); err != nil {
return nil, err
}
return c, nil
}
func (c *Connection) SendCommand(cmd string) string {
c.sendCommand(2, []byte(cmd))
pkg := c.readPkg()
return string(pkg.Body)
}
func (c *Connection) auth() error {
c.sendCommand(3, []byte(c.pass))
pkg := c.readPkg()
if pkg.Type != 2 || pkg.Id != uniqueId {
return errors.New("Incorrect password.")
}
return nil
}
func (c *Connection) sendCommand(typ int32, body []byte) {
size := int32(4 + 4 + len(body) + 2)
uniqueId += 1
id := uniqueId
wtr := binaryReadWriter{ByteOrder: binary.LittleEndian}
wtr.Write(size)
wtr.Write(id)
wtr.Write(typ)
wtr.Write(body)
wtr.Write([]byte{0x0, 0x0})
if wtr.err != nil {
log.Fatal(wtr.err)
}
c.conn.Write(wtr.buf.Bytes())
}
func (c *Connection) readPkg() Pkg {
const bufSize = 4096
b := make([]byte, bufSize)
// Doesn't handle split messages correctly.
read, err := c.conn.Read(b)
if err != nil {
log.Fatal(err)
}
p := Pkg{}
rdr := binaryReadWriter{ByteOrder: binary.LittleEndian,
buf: bytes.NewBuffer(b)}
rdr.Read(&p.Size)
rdr.Read(&p.Id)
rdr.Read(&p.Type)
body := [bufSize - 12]byte{}
rdr.Read(&body)
if rdr.err != nil {
log.Fatal(rdr.err)
}
p.Body = body[:read-12]
return p
}
type Pkg struct {
Size int32
Id int32
Type int32
Body []byte
_null int16
}
type binaryReadWriter struct {
ByteOrder binary.ByteOrder
err error
buf *bytes.Buffer
}
func (b *binaryReadWriter) Write(v interface{}) {
if b.err != nil {
return
}
if b.buf == nil {
b.buf = new(bytes.Buffer)
}
b.err = binary.Write(b.buf, b.ByteOrder, v)
}
func (b *binaryReadWriter) Read(v interface{}) {
if b.err != nil || b.buf == nil {
return
}
binary.Read(b.buf, b.ByteOrder, v)
}