Jump to content

Recommended Posts

super-truite
Posted

I would like to be able to use the remote console in code in order to be able to input commands to my server in a web interface or desktop applications (to use server input MCU's for instane).
I see it was already done by coconut here:

I am able to use the remote console using the .exe and I can send messags to my Dserver, 

but I am not able to send and recieve messages from the tcp socket in my favorite langage (python).
The issue might be because of the message formatting. In the readme of  the remote console, there is this explanation:
 

"Commands, which are sended to server, and responces, recieved from the serverver are nested into  packets. Every packet should have a structure as follows:


| length of data  |                              data                                            |
|        |                   | The string containing command or responce |00000000|
|     two bytes     |    length of string                                                   |one byte|

 So, in fact, the whole packet consists of two bytes, representing the length of data, and the data, which is a C-string (just chars, bytes), terminated with 0-byte. One command/responce is nested in one packet. So as you can see, the maximum packet data lendth is 64K (two bytes for data length)."

What I do is the following:

1) convert my command string (for instancea log in command : ""auth admin pasword") to bytes

2) compute the length of the converted string and convert it to bytes

3) combine the converted length + converted command + 0-byte

Is that corect ? I have seen this code from coconut: 
https://bitbucket.org/johdex/commander/src/default/Commander/Commander/RemoteConsole.fs
But it seems I am unable to reproduce the packet formatting.

Here is my attempt in python:

cmd = 'auth admin pasword'
cmd = bytes(cmd)
length = bytes([len(cmd)])
packet = length + cmd + b'\x00'

 

when I send this I never receive a response and my call is left hanging. Dserver sees a connexion though.
 

import socket
# connect to socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('192.168.0.14', 8991))
# packet formatting
cmd = 'auth admin password'
cmd = bytes(cmd)
length = bytes([len(cmd)])
packet = length + cmd + b'\x00'
# sending packet 
s.sendall(packet)
# get data back (hangs :( )
data = s.recv(2048)
s.close()
print('Received', repr(data))


Other potential issue: the size of the packet received ? 

Any ideas ? @coconut ? ?

 

Alternative way of doing things I would accept: using console calls if there is a way to use RconClient.exe in command line.
 


 

Posted
On 4/29/2020 at 11:25 AM, super-truite said:

length = bytes([len(cmd)])

Check how many bytes that gives you. It should be two. I suspect you are getting 4.

 

Posted

Use the struct module in Python to pack the data

super-truite
Posted

thx, I made progress! it's not hanging anymore, the length was indeed wrong and the struct module is great to simplify things. 
I did not manage yet to control my serverinput MCU's but I am on the right path. 

super-truite
Posted

I am always getting STATUS=10 as a response. This is not documented in the readme (stops at STATUS=9)? Any idea ?
I guess it is still a packaging issue for the messages. my packing/unpacking functions in python 3 :

 

def pack_message(msg):
    msg = msg.encode('ASCII')
    packet = struct.pack("H{0}sx".format(len(msg)), len(msg), msg)
    return packet 

def unpack_message(data):
    dataformat = "H{0}sx".format(struct.unpack("h", data[0:2])[0]-1)
    data = struct.unpack(dataformat, data)
    return data

packet  format: unsigned int +  len(msg)-bytes string ()  + pad byte  

The data I receive seems to not follow the format described in the readme: the length is the length of the string + 1.
Full code:

import socket
import struct 

HOST = '192.168.0.14'  
PORT = 8991        

def pack_message(msg):
    msg = msg.encode('ASCII')
    packet = struct.pack("H{0}sx".format(len(msg)), len(msg), msg)
    return packet 

def unpack_message(data):
    dataformat = "H{0}sx".format(struct.unpack("h", data[0:2])[0]-1)
    data = struct.unpack(dataformat, data)
    return data

class RemoteConsoleClient():
    def __init__(self, host, port):
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.host = host
        self.port = port
        self.connect()
    def connect(self):
        try:
            self.client.connect((self.host, self.port))
            print('connected to:', self.host, self.port)
        except socket.error as e:
            print(e)
    def send(self, msg):
        try:
            packet = pack_message(msg)
            self.client.send(packet)
            data = self.client.recv(4096)
            return unpack_message(data)
         
        except socket.error as e:
            print(e)
        
remoteconsole = RemoteConsoleClient(HOST, PORT)    
auth = remoteconsole.send('auth admin password')
print(auth)
server_input = remoteconsole.send('serverinput start')
print(server_input)

results of the print commands:

 

connected to: 192.168.0.14 8991
(10, b'STATUS=10')
[WinError 10053] Une connexion établie a été abandonnée par un logiciel de votre ordinateur hôte
None

STATUS=10 is the response to the auth command and the Winerror 10053 comes from the serverinput command which fails because of the failed authentification probably

 

  • Upvote 1
super-truite
Posted (edited)

Got it working, I was not sending the good header (it's the length of the message + 1 and not only the length of the message).

Working python 3 code:
 

import socket
import struct 

HOST = '192.168.0.14'  
PORT = 8991        

def pack_message(msg):
    msg = msg.encode('ASCII')
    packet = struct.pack("H{0}sx".format(len(msg)), len(msg)+1, msg)
    return packet 

def unpack_message(data):
    dataformat = "H{0}sx".format(struct.unpack("h", data[0:2])[0]-1)
    data = struct.unpack(dataformat, data)
    return data

class RemoteConsoleClient():
    def __init__(self, host, port):
        self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.host = host
        self.port = port
        self.connect()
    def connect(self):
        try:
            self.client.connect((self.host, self.port))
            print('connected to:', self.host, self.port)
        except socket.error as e:
            print(e)
    def send(self, msg):
        try:
            packet = pack_message(msg)
            self.client.send(packet)
            data = self.client.recv(4096)
            return unpack_message(data)[1].decode()
         
        except socket.error as e:
            print(e)
        
remoteconsole = RemoteConsoleClient(HOST, PORT)    
auth = remoteconsole.send('auth admin password')
print('Response: ', auth)
server_input = remoteconsole.send('serverinput start')
print('Response: ', server_input)

Output:

connected to: 192.168.0.14 8991
Response:  STATUS=1
Response:  STATUS=1

 

?

Thanks again lads !

 

Edit: used the tool wireshark to understand what packets goes in and out of the rconclient.exe. Great tool

Edited by super-truite
  • Thanks 1
  • 10 months later...
ITAF_LittleMadz77
Posted

Guys this doesn't work for me. 

 

I'm sorry I'm not a python guy. I have this error, beside error 6. 

 

dataformat = "H{0}sx".format(struct.unpack("h", data[0:2])[0]-1)

 

Any help would be greatly appreciated

 

Regards

 

Maurizio

super-truite
Posted (edited)

error 6 is linked to bad authentification. Did you modify the login/pwd in my example? Change admin/password in this line:
 

auth = remoteconsole.send('auth admin password')

You can find some working code in my discord bot project : https://github.com/Super-truite/il2_discord_bot

Edited by super-truite

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...