├── README.md
├── example.cpp
├── query.cpp
├── query.h
├── rcon.cpp
└── rcon.h
/README.md:
--------------------------------------------------------------------------------
1 | C++ implementation of the RCON/Query mechanism.
2 | ===========
3 |
4 | So as I was working on a C++ project, I needed to use the RCON mechanism of a SA-MP server to run some commands, so to avoid "reinventing the wheel", I went and searched around for a C++ implementation of this mechanism, sadly the only one that I found had all broken links and likely no chance for them to be fixed, so I started writing up this!
5 |
6 | How to use it.
7 | ===========
8 |
9 | It's quite simple actually, I've broken it up into a couple classes and here's the gist of it...
10 |
11 | Sending an "echo" RCON command to a server on localhost (127.0.0.1), port 7777 with password changeme, then recieving the response from the server and printing it into the console:
12 | ```cpp
13 | RCON rcon("127.0.0.1", 7777, "changeme");
14 | rcon.Send("echo Hello there!");
15 | std::string recvval = rcon.Recv();
16 | std::cout << "RCON Response: " << recvval << "\n";
17 | ```
18 |
19 | Sending a ping request to a server on localhost (127.0.0.1), port 7777 then recieving the response and printing it into the console:
20 | ```cpp
21 | Query query("127.0.0.1", 7777);
22 | std::string recvval = query.Ping("5256");
23 | std::cout << "Ping Response: " << recvval << "\n";
24 | ```
25 |
26 | Pretty simple, right? For a more detailed example see "example.cpp".
27 |
28 | The functions.
29 | ===========
30 |
31 | RCON:
32 | ```cpp
33 | int Send(std::string command); // Send an RCON command to the IP/port used during construction.
34 | std::string Recv(); // Recieve responses from the IP/port used during construction.
35 |
36 | RCON(std::string ip, const short port, std::string password); // Construct the RCON class with the specified IP, port and password.
37 | ~RCON(); // Destruct the RCON class.
38 | ```
39 |
40 | Query:
41 | ```cpp
42 | std::string Information(); // Get information of the server on the IP/port used during construction.
43 | std::string Rules(); // Get the rules of the server on the IP/port used during construction.
44 | std::string ClientList(); // Get the client list of the server on the IP/port used during construction.
45 | std::string DetailedPlayerInfo(); // Get detailed client list of the server on the IP/port used during construction.
46 | std::string Ping(std::string data); // Send a ping request with the data (4 psuedo-random characters) to the server on the IP/port used during construction.
47 |
48 | int Send(const char opcode, std::string data); // Send a request to the server on the IP/port specified during construction.
49 | std::string Recv(); // Get responses from the server on the IP/port specified during construction.
50 |
51 | Query(std::string ip, const short port); // Construct the Query class with the IP/port specified.
52 | ~Query(); // Destruct the Query class.
53 | ```
54 |
55 | The end.
56 | ===========
57 |
58 | Go ahead and check it out, if you see any flaws/bugs or room for improvement either make an issue on the repository or even fork the repository, make the changes and then fire off a pull request, I'll review the changes asap.
59 |
--------------------------------------------------------------------------------
/example.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | C++ Implementation of the SA-MP RCON/Query mechanism.
3 |
4 | Copyright (C) 2013 Cody Cunningham
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 | #include "rcon.h"
20 | #include "query.h"
21 |
22 | #define RCON_EXAMPLE // Comment (//) out this line if you do not want the RCON example.
23 | #define QUERY_EXAMPLE // Comment (//) out this line if you do not want the Query example.
24 |
25 | #define EXAMPLE_IP "127.0.0.1" // The IP of the server.
26 | #define EXAMPLE_PORT 7777 // The port of the server.
27 | #define EXAMPLE_PASSWORD "changeme" // The RCON password of the server.
28 | #define EXAMPLE_COMMAND "cmdlist" // The RCON command you want to execute on the above server.
29 |
30 | int main()
31 | {
32 | std::string data;
33 |
34 | // RCON Example.
35 | #ifdef RCON_EXAMPLE
36 | RCON rcon(EXAMPLE_IP, EXAMPLE_PORT, EXAMPLE_PASSWORD);
37 | rcon.Send(EXAMPLE_COMMAND);
38 | data.assign(rcon.Recv());
39 | std::cout << "RCON response: " << data << "\n";
40 | #endif
41 |
42 | #ifdef QUERY_EXAMPLE
43 | // Query example.
44 | Query query(EXAMPLE_IP, EXAMPLE_PORT);
45 | std::string pingdata;
46 | std::stringstream tostring;
47 | tostring << ((rand() % 8999) + 1000);
48 | data.assign(query.Ping(tostring.str()));
49 | std::cout << "Query response: " << data << "\n";
50 | #endif
51 | return 1;
52 | }
--------------------------------------------------------------------------------
/query.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | C++ Implementation of the SA-MP RCON/Query mechanism.
3 |
4 | Copyright (C) 2013 Cody Cunningham
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 | #include "query.h"
20 |
21 | #ifndef WIN32
22 | unsigned long long int Query::GetTickCount()
23 | {
24 | std::ifstream uptime("/proc/uptime");
25 | if(uptime)
26 | {
27 | char *uptimebuf = new char[128];
28 | uptime.read(uptimebuf, 128);
29 |
30 | for(int i = 0; i < 128; i++)
31 | {
32 | if(uptimebuf[i] == ' ' || (uptimebuf[i - 1] == '\0' && i > 1))
33 | {
34 | uptimebuf[i] = '\0';
35 | }
36 | }
37 | double time = atof(uptimebuf);
38 |
39 | delete[] uptimebuf;
40 | return (time * 1000);
41 | }
42 | else
43 | {
44 | struct timeb tp;
45 | ftime(&tp);
46 | return ((tp.time * 1000) + tp.millitm);
47 | }
48 | }
49 | #endif
50 |
51 | std::string Query::Information(int timeout)
52 | {
53 | if(Send('i') < 0) return NULL;
54 | return Recv(timeout);
55 | }
56 |
57 | std::string Query::Rules(int timeout)
58 | {
59 | if(Send('r') < 0) return NULL;
60 | return Recv(timeout);
61 | }
62 |
63 | std::string Query::ClientList(int timeout)
64 | {
65 | if(Send('c') < 0) return NULL;
66 | return Recv(timeout);
67 | }
68 |
69 | std::string Query::DetailedPlayerInfo(int timeout)
70 | {
71 | if(Send('d') < 0) return NULL;
72 | return Recv(timeout);
73 | }
74 |
75 | std::string Query::Ping(std::string data, int timeout)
76 | {
77 | if(Send('p', data) < 0) return NULL;
78 | return Recv(timeout);
79 | }
80 |
81 | int Query::Send(const char opcode, std::string data)
82 | {
83 | if(sock < 1) return -1;
84 |
85 | std::string packet = Assemble(opcode, data);
86 | if(packet.length() < 4) return -1;
87 |
88 | return sendto(sock, packet.c_str(), packet.length(), 0, (sockaddr*)&server, sizeof(sockaddr_in));
89 | }
90 |
91 | std::string Query::Recv(int timeout)
92 | {
93 | if(sock < 1) return NULL;
94 |
95 | std::string packet;
96 | char cbuffer[512];
97 | memset(cbuffer, '\0', 512);
98 |
99 | sockaddr_in from;
100 | socklen_t fromlen = sizeof(from);
101 | int recvbytes = 0;
102 | unsigned long long int starttime = GetTickCount();
103 |
104 | while(GetTickCount() - starttime < (unsigned long long int)timeout)
105 | {
106 | recvbytes = recvfrom(sock, cbuffer, 512, 0, (sockaddr*)&from, &fromlen);
107 | if(recvbytes > 11)
108 | {
109 | if(packet.length() > 0) packet.append("\n");
110 |
111 | int state = 0;
112 | for(int i = 11; i < recvbytes; i++)
113 | {
114 | if(!(cbuffer[i] >= 32 && cbuffer[i] <= 126) && state == 0)
115 | {
116 | state = 1;
117 | packet.append("\n");
118 | }
119 | else if((cbuffer[i] >= 32 && cbuffer[i] <= 126))
120 | {
121 | state = 0;
122 | packet += cbuffer[i];
123 | }
124 | }
125 |
126 | starttime = GetTickCount();
127 | memset(cbuffer, '\0', 512);
128 | }
129 | }
130 | return packet;
131 | }
132 |
133 | std::string Query::Assemble(const char opcode, std::string data)
134 | {
135 | if(sip.length() < 4 || sport < 1) return NULL;
136 |
137 | std::string packet("SAMP");
138 |
139 | // Split the IP into 4 bytes.
140 | std::stringstream it(sip);
141 | std::string part;
142 | while(std::getline(it, part, '.'))
143 | {
144 | packet += (BYTE)atoi(part.c_str());
145 | }
146 |
147 | // Split the port into 2 bytes.
148 | packet += (BYTE)(sport & 0xFF);
149 | packet += (BYTE)(sport >> 8 & 0xFF);
150 |
151 | packet += opcode;
152 |
153 | if(data.length() > 0)
154 | {
155 | packet.append(data);
156 | }
157 | return packet;
158 | }
159 |
160 | Query::Query(std::string ip, const short port)
161 | {
162 | #ifdef WIN32
163 | WSADATA wsa;
164 | if(WSAStartup(MAKEWORD(2, 2), &wsa)) return; // Failed to start up WSA.
165 | #endif
166 |
167 | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
168 | if(sock < 1) // Failed to create socket.
169 | {
170 | std::cerr << "Failed to create socket.\n";
171 | sock = 0;
172 | #ifdef WIN32
173 | WSACleanup();
174 | #endif
175 | return;
176 | }
177 |
178 | #ifdef WIN32
179 | DWORD toggle = 1;
180 | if (ioctlsocket(sock, FIONBIO, &toggle) < 0)
181 | {
182 | std::cerr << "Failed to set socket to non-blocking.\n";
183 | closesocket(sock);
184 | sock = 0;
185 | #ifdef WIN32
186 | WSACleanup();
187 | #endif
188 | return;
189 | }
190 | #else
191 | int toggle = 1;
192 | if (fcntl(sock, F_SETFL, O_NONBLOCK, toggle) < 0)
193 | {
194 | std::cerr << "Failed to set socket to non-blocking.\n";
195 | closesocket(sock);
196 | sock = 0;
197 | #ifdef WIN32
198 | WSACleanup();
199 | #endif
200 | return;
201 | }
202 | #endif
203 |
204 | memset((void*)&server, '\0', sizeof(sockaddr_in));
205 |
206 | server.sin_family = AF_INET;
207 | server.sin_port = htons(port);
208 | server.sin_addr.s_addr = inet_addr(ip.c_str());
209 |
210 | // For the sake of Assemble
211 | sip.assign(ip);
212 | sport = port;
213 | }
214 |
215 | Query::~Query()
216 | {
217 | closesocket(sock);
218 | }
--------------------------------------------------------------------------------
/query.h:
--------------------------------------------------------------------------------
1 | /*
2 | C++ Implementation of the SA-MP RCON/Query mechanism.
3 |
4 | Copyright (C) 2013 Cody Cunningham
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #ifdef WIN32
25 | #include
26 | #include
27 | #pragma comment(lib, "Ws2_32.lib")
28 |
29 | typedef int socklen_t;
30 | #else
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | #include
37 | #include
38 |
39 | typedef unsigned char BYTE;
40 | #define closesocket close
41 | #endif
42 |
43 | #define QUERY_DEFAULT_TIMEOUT 100
44 |
45 | class Query
46 | {
47 | public:
48 | std::string Information(int timeout = QUERY_DEFAULT_TIMEOUT);
49 | std::string Rules(int timeout = QUERY_DEFAULT_TIMEOUT);
50 | std::string ClientList(int timeout = QUERY_DEFAULT_TIMEOUT);
51 | std::string DetailedPlayerInfo(int timeout = QUERY_DEFAULT_TIMEOUT);
52 | std::string Ping(std::string data, int timeout = QUERY_DEFAULT_TIMEOUT);
53 |
54 | // Shouldn't really be required, but here if you need it.
55 | int Send(const char opcode, std::string data = " ");
56 | std::string Recv(int timeout = QUERY_DEFAULT_TIMEOUT);
57 |
58 | Query(std::string ip, const short port);
59 | ~Query();
60 |
61 | private:
62 | int sock;
63 | struct sockaddr_in server;
64 |
65 | std::string Assemble(const char opcode, std::string data);
66 | std::string sip;
67 | short sport;
68 |
69 | #ifndef WIN32
70 | unsigned long long int GetTickCount();
71 | #endif
72 | };
--------------------------------------------------------------------------------
/rcon.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | C++ Implementation of the SA-MP RCON/Query mechanism.
3 |
4 | Copyright (C) 2013 Cody Cunningham
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 | #include "rcon.h"
20 |
21 | #ifndef WIN32
22 | unsigned long long int RCON::GetTickCount()
23 | {
24 | std::ifstream uptime("/proc/uptime");
25 | if(uptime)
26 | {
27 | char *uptimebuf = new char[128];
28 | uptime.read(uptimebuf, 128);
29 |
30 | for(int i = 0; i < 128; i++)
31 | {
32 | if(uptimebuf[i] == ' ' || (uptimebuf[i - 1] == '\0' && i > 1))
33 | {
34 | uptimebuf[i] = '\0';
35 | }
36 | }
37 | double time = atof(uptimebuf);
38 |
39 | delete[] uptimebuf;
40 | return (time * 1000);
41 | }
42 | else
43 | {
44 | struct timeb tp;
45 | ftime(&tp);
46 | return ((tp.time * 1000) + tp.millitm);
47 | }
48 | }
49 | #endif
50 |
51 | int RCON::Send(std::string command)
52 | {
53 | if(sock < 1) return -1;
54 |
55 | std::string packet = Assemble(command);
56 | if(packet.length() < 4) return -1;
57 |
58 | return sendto(sock, packet.c_str(), packet.length(), 0, (sockaddr*)&server, sizeof(sockaddr_in));
59 | }
60 |
61 | std::string RCON::Recv(int timeout)
62 | {
63 | if(sock < 1) return NULL;
64 |
65 | std::string packet;
66 | char cbuffer[512];
67 | memset(cbuffer, '\0', 512);
68 |
69 | sockaddr_in from;
70 | socklen_t fromlen = sizeof(from);
71 | int recvbytes = 0;
72 | unsigned long long int starttime = GetTickCount();
73 |
74 | while(GetTickCount() - starttime < (unsigned long long int)timeout)
75 | {
76 | recvbytes = recvfrom(sock, cbuffer, 512, 0, (sockaddr*)&from, &fromlen);
77 | if(recvbytes > 13)
78 | {
79 | if(packet.length() > 0) packet.append("\n");
80 |
81 | int state = 0;
82 | for(int i = 13; i < recvbytes; i++)
83 | {
84 | if(!(cbuffer[i] >= 32 && cbuffer[i] <= 126) && state == 0)
85 | {
86 | state = 1;
87 | packet.append("\n");
88 | }
89 | else if((cbuffer[i] >= 32 && cbuffer[i] <= 126))
90 | {
91 | state = 0;
92 | packet += cbuffer[i];
93 | }
94 | }
95 | starttime = GetTickCount();
96 | memset(cbuffer, '\0', 512);
97 | }
98 | }
99 | return packet;
100 | }
101 |
102 | std::string RCON::Assemble(std::string data)
103 | {
104 | if(sip.length() < 4 || sport < 1) return NULL;
105 |
106 | std::string packet("SAMP");
107 |
108 | // Split the IP into 4 bytes and append it to the packet.
109 | std::stringstream it(sip);
110 | std::string part;
111 | while(std::getline(it, part, '.'))
112 | {
113 | packet += (BYTE)atoi(part.c_str());
114 | }
115 |
116 | // Split the port into 2 bytes and append it to the packet.
117 | packet += (BYTE)(sport & 0xFF);
118 | packet += (BYTE)(sport >> 8 & 0xFF);
119 |
120 | // Append the RCON opcode to the packet.
121 | packet += 'x';
122 |
123 | // Split the password length into 2 bytes and append it to the packet.
124 | packet += (BYTE)(spass.length() & 0xFF);
125 | packet += (BYTE)(spass.length() >> 8 & 0xFF);
126 |
127 | // Append the password to the packet.
128 | packet.append(spass);
129 |
130 | // Split the data length into 2 bytes and append it to the packet.
131 | packet += (BYTE)(data.length() & 0xFF);
132 | packet += (BYTE)(data.length() >> 8 & 0xFF);
133 |
134 | // Append the data on to the packet.
135 | packet.append(data);
136 |
137 | return packet;
138 | }
139 |
140 | RCON::RCON(std::string ip, const short port, std::string password)
141 | {
142 | #ifdef WIN32
143 | WSADATA wsa;
144 | if(WSAStartup(MAKEWORD(2, 2), &wsa)) return; // Failed to start up WSA.
145 | #endif
146 |
147 | sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
148 | if(sock < 1) // Failed to create socket.
149 | {
150 | std::cerr << "Failed to create socket.\n";
151 | sock = 0;
152 | #ifdef WIN32
153 | WSACleanup();
154 | #endif
155 | return;
156 | }
157 |
158 | #ifdef WIN32
159 | DWORD toggle = 1;
160 | if (ioctlsocket(sock, FIONBIO, &toggle) < 0)
161 | {
162 | std::cerr << "Failed to set socket to non-blocking.\n";
163 | closesocket(sock);
164 | sock = 0;
165 | #ifdef WIN32
166 | WSACleanup();
167 | #endif
168 | return;
169 | }
170 | #else
171 | int toggle = 1;
172 | if (fcntl(sock, F_SETFL, O_NONBLOCK, toggle) < 0)
173 | {
174 | std::cerr << "Failed to set socket to non-blocking.\n";
175 | closesocket(sock);
176 | sock = 0;
177 | #ifdef WIN32
178 | WSACleanup();
179 | #endif
180 | return;
181 | }
182 | #endif
183 |
184 | memset((void*)&server, '\0', sizeof(sockaddr_in));
185 |
186 | server.sin_family = AF_INET;
187 | server.sin_port = htons(port);
188 | server.sin_addr.s_addr = inet_addr(ip.c_str());
189 |
190 | // For the sake of Assemble
191 | sip.assign(ip);
192 | spass.assign(password);
193 | sport = port;
194 |
195 | Send("echo RCON admin connected to the server.");
196 | }
197 |
198 | RCON::~RCON()
199 | {
200 | closesocket(sock);
201 | }
--------------------------------------------------------------------------------
/rcon.h:
--------------------------------------------------------------------------------
1 | /*
2 | C++ Implementation of the SA-MP RCON/Query mechanism.
3 |
4 | Copyright (C) 2013 Cody Cunningham
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #ifdef WIN32
25 | #include
26 | #include
27 | #pragma comment(lib, "Ws2_32.lib")
28 |
29 | typedef int socklen_t;
30 | #else
31 | #include
32 | #include
33 | #include
34 | #include
35 |
36 | #include
37 | #include
38 |
39 | typedef unsigned char BYTE;
40 | #define closesocket close
41 | #endif
42 |
43 |
44 | #define RCON_DEFAULT_TIMEOUT 100
45 |
46 | class RCON
47 | {
48 | public:
49 | int Send(std::string command);
50 | std::string Recv(int timeout = RCON_DEFAULT_TIMEOUT);
51 |
52 | RCON(std::string ip, const short port, std::string password);
53 | ~RCON();
54 |
55 | private:
56 | int sock;
57 | struct sockaddr_in server;
58 |
59 | std::string Assemble(std::string data);
60 | std::string sip;
61 | std::string spass;
62 | short sport;
63 |
64 | #ifndef WIN32
65 | unsigned long long int GetTickCount();
66 | #endif
67 | };
--------------------------------------------------------------------------------