Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rcon #2

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
added exec rcon functionality
  • Loading branch information
kunalpowar committed Nov 22, 2014
commit fb1868f066f209582c8a09325d69c7afc1113476
41 changes: 41 additions & 0 deletions rconRequest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package steam

import (
"bytes"

"math/rand"
)

const (
SERVERDATA_AUTH = 3
SERVERDATA_EXECCOMMAND = 2
SERVERDATA_AUTH_RESPONSE = 2
SERVERDATA_RESPONSE_VALUE = 0
)

type rconRequest struct {
size int32
id int32
reqType int32
body string
}

func newrconRequest(reqType int32, body string) *rconRequest {
return &rconRequest{
size: int32(len(body) + 10),
id: rand.Int31(),
reqType: reqType,
body: body,
}
}

func (r *rconRequest) constructPacket() []byte {
buf := new(bytes.Buffer)
writeLilEndianInt32(buf, r.size)
writeLilEndianInt32(buf, r.id)
writeLilEndianInt32(buf, r.reqType)
buf.WriteString(r.body)
writeNullTerminator(buf)
writeNullTerminator(buf)
return buf.Bytes()
}
26 changes: 26 additions & 0 deletions rconresponse.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package steam

import (
"bytes"

"github.com/golang/glog"
)

type rconResponse struct {
size int32
id int32
reqType int32
body string
}

func newRconResponse(b []byte) *rconResponse {
buf := bytes.NewBuffer(b)
s := readLong(buf)
id := readLong(buf)
t := readLong(buf)
body := readBytes(buf, int(s-8))

resp := &rconResponse{s, id, t, string(body)}
glog.V(2).Infof("steam: rconResponse: %v", resp)
return resp
}
28 changes: 28 additions & 0 deletions samples/samplercon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"flag"
"fmt"

"github.com/kidoman/go-steam"
)

func main() {
flag.Parse()
server := &steam.Server{Addr: "0.1.2.3:27015"}
defer server.Close()

authenticated, err := server.AuthenticateRcon("abc")
if err != nil {
panic(err)
}
fmt.Printf("authentication status %v\n", authenticated)

comm := "status"
resp, err := server.ExecRconCommand(comm)
if err != nil {
panic(err)
}

fmt.Printf("rcon command: %v response: %v\n", comm, resp)
}
84 changes: 73 additions & 11 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ package steam
import (
"errors"
"time"

"github.com/golang/glog"
)

// Server represents a Source server.
type Server struct {
// IP:Port combination designating a single server.
Addr string

socket *socket
udpSocket *udpSocket

tcpSocket *tcpSocket

initialized bool
}
Expand All @@ -25,7 +29,11 @@ func (s *Server) init() error {
}

var err error
if s.socket, err = newSocket(s.Addr); err != nil {
if s.udpSocket, err = newUdpSocket(s.Addr); err != nil {
return err
}

if s.tcpSocket, err = newTcpSocket(s.Addr); err != nil {
return err
}

Expand All @@ -39,7 +47,8 @@ func (s *Server) Close() {
return
}

s.socket.close()
s.udpSocket.close()
s.tcpSocket.close()
}

// Ping returns the RTT (round-trip time) to the server.
Expand All @@ -54,8 +63,8 @@ func (s *Server) Ping() (time.Duration, error) {
}

start := time.Now()
s.socket.send(data)
if _, err := s.socket.receive(); err != nil {
s.udpSocket.send(data)
if _, err := s.udpSocket.receive(); err != nil {
return 0, err
}

Expand All @@ -74,10 +83,10 @@ func (s *Server) Info() (*InfoResponse, error) {
return nil, err
}

if err := s.socket.send(data); err != nil {
if err := s.udpSocket.send(data); err != nil {
return nil, err
}
b, err := s.socket.receive()
b, err := s.udpSocket.receive()
if err != nil {
return nil, err
}
Expand All @@ -101,10 +110,10 @@ func (s *Server) PlayersInfo() (*PlayersInfoResponse, error) {
if err != nil {
return nil, err
}
if err := s.socket.send(data); err != nil {
if err := s.udpSocket.send(data); err != nil {
return nil, err
}
b, err := s.socket.receive()
b, err := s.udpSocket.receive()
if err != nil {
return nil, err
}
Expand All @@ -121,10 +130,10 @@ func (s *Server) PlayersInfo() (*PlayersInfoResponse, error) {
if err != nil {
return nil, err
}
if err := s.socket.send(data); err != nil {
if err := s.udpSocket.send(data); err != nil {
return nil, err
}
b, err = s.socket.receive()
b, err = s.udpSocket.receive()
if err != nil {
return nil, err
}
Expand All @@ -138,3 +147,56 @@ func (s *Server) PlayersInfo() (*PlayersInfoResponse, error) {

return res, nil
}

func (s *Server) AuthenticateRcon(rconpasswd string) (bool, error) {
if err := s.init(); err != nil {
return false, err
}

req := newrconRequest(SERVERDATA_AUTH, rconpasswd)
glog.V(2).Infof("steam: sending rcon auth request: %v", req)
packet := req.constructPacket()

if err := s.tcpSocket.send(packet); err != nil {
return false, err
}

resp, err := s.tcpSocket.receive()
if err != nil {
return false, err
}

authResponse := newRconResponse(resp)
if req.id == authResponse.id {
return true, nil
}
return false, nil
}

func (s *Server) ExecRconCommand(command string) (result string, err error) {
if err := s.init(); err != nil {
return "", err
}

req := newrconRequest(SERVERDATA_EXECCOMMAND, command)
glog.V(2).Infof("steam: sending rcon exec command request: %v", req)
packet := req.constructPacket()

if err := s.tcpSocket.send(packet); err != nil {
return "", err
}

resp, err := s.tcpSocket.receive()
if err != nil {
return "", err
}

commandResp := newRconResponse(resp)

if req.id != commandResp.id {
err := errors.New("steam: response id does not match request id")
return "", err
}

return commandResp.body, nil
}
57 changes: 57 additions & 0 deletions tcpsocket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package steam

import (
"net"

"github.com/golang/glog"
)

type tcpSocket struct {
conn *net.TCPConn
raddr *net.TCPAddr
}

func newTcpSocket(addr string) (*tcpSocket, error) {

raddr, err := net.ResolveTCPAddr("tcp4", addr)
if err != nil {
glog.Errorf("steam: could not resolve tcp addr coz: %v", err.Error())
return nil, err
}

conn, err := net.DialTCP("tcp4", nil, raddr)
if err != nil {
glog.Errorf("steam: could not dial tcp coz: %v", err.Error())
return nil, err
}

glog.V(2).Infof("steam: succesfully created tcp connection. conn:%v, raddr: %v", conn, raddr)
return &tcpSocket{conn, raddr}, nil
}

func (s *tcpSocket) close() {
glog.V(2).Infof("steam: closing tcp connection")
s.conn.Close()
}

func (s *tcpSocket) send(payload []byte) error {
glog.V(2).Infof("steam: sending payload %v", payload)
_, err := s.conn.Write(payload)
if err != nil {
glog.V(2).Infof("steam: error sending data: %v", err.Error())
}
return nil
}

func (s *tcpSocket) receive() ([]byte, error) {
var buf [4095]byte
glog.V(1).Infof("steam: reading from %v", s.raddr)

n, err := s.conn.Read(buf[:])
if err != nil {
return nil, err
}
glog.V(1).Infof("steam: received %v bytes from %v", n, s.raddr)

return buf[:n], nil
}
14 changes: 7 additions & 7 deletions socket.go → udpsocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import (
"github.com/golang/glog"
)

type socket struct {
type udpSocket struct {
conn *net.UDPConn
raddr *net.UDPAddr
}

func newSocket(addr string) (*socket, error) {
func newUdpSocket(addr string) (*udpSocket, error) {
raddr, err := net.ResolveUDPAddr("udp4", addr)
if err != nil {
return nil, err
Expand All @@ -23,14 +23,14 @@ func newSocket(addr string) (*socket, error) {
return nil, err
}

return &socket{conn, raddr}, nil
return &udpSocket{conn, raddr}, nil
}

func (s *socket) close() {
func (s *udpSocket) close() {
s.conn.Close()
}

func (s *socket) send(payload []byte) error {
func (s *udpSocket) send(payload []byte) error {
glog.V(1).Infof("steam: sending %v bytes payload to %v", len(payload), s.raddr)
glog.V(2).Infof("steam: sending payload to %v: %X", s.raddr, payload)
n, err := s.conn.WriteToUDP(payload, s.raddr)
Expand All @@ -44,7 +44,7 @@ func (s *socket) send(payload []byte) error {
return nil
}

func (s *socket) receivePacket() ([]byte, error) {
func (s *udpSocket) receivePacket() ([]byte, error) {
var buf [1500]byte
n, _, err := s.conn.ReadFromUDP(buf[:])
if err != nil {
Expand All @@ -56,7 +56,7 @@ func (s *socket) receivePacket() ([]byte, error) {
return buf[:n], nil
}

func (s *socket) receive() ([]byte, error) {
func (s *udpSocket) receive() ([]byte, error) {
buf, err := s.receivePacket()
if err != nil {
return nil, err
Expand Down
9 changes: 9 additions & 0 deletions wire.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,12 @@ func writeLong(buf *bytes.Buffer, v int32) {
bytes := [4]byte{byte(v & 0xFF), byte(v >> 8 & 0xFF), byte(v >> 16 & 0xFF), byte(v >> 24 & 0xFF)}
buf.Write(bytes[:])
}

func writeLilEndianInt32(buf *bytes.Buffer, n int32) {
var b = []uint8{uint8(n), uint8(n >> 8), uint8(n >> 16), uint8(n >> 24)}
buf.Write(b)
}

func writeNullTerminator(buf *bytes.Buffer) {
buf.WriteByte(0)
}