├── 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 | }; --------------------------------------------------------------------------------