OpenTTD
network_admin.cpp
Go to the documentation of this file.
1 /* $Id: network_admin.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
6  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
7  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
8  */
9 
12 #ifdef ENABLE_NETWORK
13 
14 #include "../stdafx.h"
15 #include "../strings_func.h"
16 #include "../date_func.h"
17 #include "network_admin.h"
18 #include "network_base.h"
19 #include "network_server.h"
20 #include "../command_func.h"
21 #include "../company_base.h"
22 #include "../console_func.h"
23 #include "../core/pool_func.hpp"
24 #include "../map_func.h"
25 #include "../rev.h"
26 #include "../game/game.hpp"
27 
28 #include "../safeguards.h"
29 
30 
31 /* This file handles all the admin network commands. */
32 
35 
38 
41 INSTANTIATE_POOL_METHODS(NetworkAdminSocket)
42 
43 
44 static const int ADMIN_AUTHORISATION_TIMEOUT = 10000;
45 
46 
59 };
62 
68 {
72 }
73 
78 {
80  DEBUG(net, 1, "[admin] '%s' (%s) has disconnected", this->admin_name, this->admin_version);
82 }
83 
89 {
91  /* We can't go over the MAX_ADMINS limit here. However, if we accept
92  * the connection, there has to be space in the pool. */
93  assert_compile(NetworkAdminSocketPool::MAX_SIZE == MAX_ADMINS);
95  return accept;
96 }
97 
100 {
104  DEBUG(net, 1, "[admin] Admin did not send its authorisation within %d seconds", ADMIN_AUTHORISATION_TIMEOUT / 1000);
105  as->CloseConnection(true);
106  continue;
107  }
108  if (as->writable) {
109  as->SendPackets();
110  }
111  }
112 }
113 
119 /* static */ void ServerNetworkAdminSocketHandler::AcceptConnection(SOCKET s, const NetworkAddress &address)
120 {
122  as->address = address; // Save the IP of the client
123 }
124 
125 /***********
126  * Sending functions for admin network
127  ************/
128 
134 {
136 
137  p->Send_uint8(error);
138  this->SendPacket(p);
139 
140  char str[100];
141  StringID strid = GetNetworkErrorMsg(error);
142  GetString(str, strid, lastof(str));
143 
144  DEBUG(net, 1, "[admin] the admin '%s' (%s) made an error and has been disconnected. Reason: '%s'", this->admin_name, this->admin_version, str);
145 
146  return this->CloseConnection(true);
147 }
148 
151 {
153 
154  /* announce the protocol version */
156 
157  for (int i = 0; i < ADMIN_UPDATE_END; i++) {
158  p->Send_bool (true);
159  p->Send_uint16(i);
161  }
162 
163  p->Send_bool(false);
164  this->SendPacket(p);
165 
166  return this->SendWelcome();
167 }
168 
171 {
173 
175  p->Send_string(_openttd_revision);
177 
182  p->Send_uint16(MapSizeX());
183  p->Send_uint16(MapSizeY());
184 
185  this->SendPacket(p);
186 
188 }
189 
192 {
194  this->SendPacket(p);
196 }
197 
200 {
202  this->SendPacket(p);
204 }
205 
208 {
210 
211  p->Send_uint32(_date);
212  this->SendPacket(p);
213 
215 }
216 
222 {
224 
225  p->Send_uint32(client_id);
226  this->SendPacket(p);
227 
229 }
230 
237 {
238  /* Only send data when we're a proper client, not just someone trying to query the server. */
239  if (ci == NULL) return NETWORK_RECV_STATUS_OKAY;
240 
242 
243  p->Send_uint32(ci->client_id);
244  p->Send_string(cs == NULL ? "" : const_cast<NetworkAddress &>(cs->client_address).GetHostname());
245  p->Send_string(ci->client_name);
246  p->Send_uint8 (ci->client_lang);
247  p->Send_uint32(ci->join_date);
248  p->Send_uint8 (ci->client_playas);
249 
250  this->SendPacket(p);
251 
253 }
254 
255 
261 {
263 
264  p->Send_uint32(ci->client_id);
265  p->Send_string(ci->client_name);
266  p->Send_uint8 (ci->client_playas);
267 
268  this->SendPacket(p);
269 
271 }
272 
278 {
280 
281  p->Send_uint32(client_id);
282  this->SendPacket(p);
283 
285 }
286 
293 {
295 
296  p->Send_uint32(client_id);
297  p->Send_uint8 (error);
298  this->SendPacket(p);
299 
301 }
302 
308 {
310  p->Send_uint8(company_id);
311 
312  this->SendPacket(p);
313 
315 }
316 
322 {
323  char company_name[NETWORK_COMPANY_NAME_LENGTH];
324  char manager_name[NETWORK_COMPANY_NAME_LENGTH];
325 
326  SetDParam(0, c->index);
327  GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
328 
329  SetDParam(0, c->index);
330  GetString(manager_name, STR_PRESIDENT_NAME, lastof(manager_name));
331 
333 
334  p->Send_uint8 (c->index);
335  p->Send_string(company_name);
336  p->Send_string(manager_name);
337  p->Send_uint8 (c->colour);
340  p->Send_bool (c->is_ai);
341  p->Send_uint8 (CeilDiv(c->months_of_bankruptcy, 3)); // send as quarters_of_bankruptcy
342 
343  for (size_t i = 0; i < lengthof(c->share_owners); i++) {
344  p->Send_uint8(c->share_owners[i]);
345  }
346 
347  this->SendPacket(p);
348 
350 }
351 
352 
358 {
359  char company_name[NETWORK_COMPANY_NAME_LENGTH];
360  char manager_name[NETWORK_COMPANY_NAME_LENGTH];
361 
362  SetDParam(0, c->index);
363  GetString(company_name, STR_COMPANY_NAME, lastof(company_name));
364 
365  SetDParam(0, c->index);
366  GetString(manager_name, STR_PRESIDENT_NAME, lastof(manager_name));
367 
369 
370  p->Send_uint8 (c->index);
371  p->Send_string(company_name);
372  p->Send_string(manager_name);
373  p->Send_uint8 (c->colour);
375  p->Send_uint8 (CeilDiv(c->months_of_bankruptcy, 3)); // send as quarters_of_bankruptcy
376 
377  for (size_t i = 0; i < lengthof(c->share_owners); i++) {
378  p->Send_uint8(c->share_owners[i]);
379  }
380 
381  this->SendPacket(p);
382 
384 }
385 
392 {
394 
395  p->Send_uint8(company_id);
396  p->Send_uint8(acrr);
397 
398  this->SendPacket(p);
399 
401 }
402 
405 {
406  const Company *company;
407  FOR_ALL_COMPANIES(company) {
408  /* Get the income. */
409  Money income = 0;
410  for (uint i = 0; i < lengthof(company->yearly_expenses[0]); i++) {
411  income -= company->yearly_expenses[0][i];
412  }
413 
415 
416  p->Send_uint8(company->index);
417 
418  /* Current information. */
419  p->Send_uint64(company->money);
420  p->Send_uint64(company->current_loan);
421  p->Send_uint64(income);
422  p->Send_uint16(min(UINT16_MAX, company->cur_economy.delivered_cargo.GetSum<OverflowSafeInt64>()));
423 
424  /* Send stats for the last 2 quarters. */
425  for (uint i = 0; i < 2; i++) {
426  p->Send_uint64(company->old_economy[i].company_value);
428  p->Send_uint16(min(UINT16_MAX, company->old_economy[i].delivered_cargo.GetSum<OverflowSafeInt64>()));
429  }
430 
431  this->SendPacket(p);
432  }
433 
434 
436 }
437 
440 {
441  /* Fetch the latest version of the stats. */
442  NetworkCompanyStats company_stats[MAX_COMPANIES];
443  NetworkPopulateCompanyStats(company_stats);
444 
445  const Company *company;
446 
447  /* Go through all the companies. */
448  FOR_ALL_COMPANIES(company) {
450 
451  /* Send the information. */
452  p->Send_uint8(company->index);
453 
454  for (uint i = 0; i < NETWORK_VEH_END; i++) {
455  p->Send_uint16(company_stats[company->index].num_vehicle[i]);
456  }
457 
458  for (uint i = 0; i < NETWORK_VEH_END; i++) {
459  p->Send_uint16(company_stats[company->index].num_station[i]);
460  }
461 
462  this->SendPacket(p);
463  }
464 
466 }
467 
476 NetworkRecvStatus ServerNetworkAdminSocketHandler::SendChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data)
477 {
479 
480  p->Send_uint8 (action);
481  p->Send_uint8 (desttype);
482  p->Send_uint32(client_id);
483  p->Send_string(msg);
484  p->Send_uint64(data);
485 
486  this->SendPacket(p);
488 }
489 
495 {
497 
498  p->Send_string(command);
499  this->SendPacket(p);
500 
502 }
503 
510 {
512 
513  p->Send_uint16(colour);
514  p->Send_string(result);
515  this->SendPacket(p);
516 
518 }
519 
521 {
522  if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
523 
524  char command[NETWORK_RCONCOMMAND_LENGTH];
525 
526  p->Recv_string(command, sizeof(command));
527 
528  DEBUG(net, 2, "[admin] Rcon command from '%s' (%s): '%s'", this->admin_name, this->admin_version, command);
529 
531  IConsoleCmdExec(command);
533  return this->SendRconEnd(command);
534 }
535 
537 {
538  if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
539 
541 
542  p->Recv_string(json, sizeof(json));
543 
544  DEBUG(net, 2, "[admin] GameScript JSON from '%s' (%s): '%s'", this->admin_name, this->admin_version, json);
545 
546  Game::NewEvent(new ScriptEventAdminPort(json));
548 }
549 
551 {
552  if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
553 
554  uint32 d1 = p->Recv_uint32();
555 
556  DEBUG(net, 2, "[admin] Ping from '%s' (%s): '%d'", this->admin_name, this->admin_version, d1);
557 
558  return this->SendPong(d1);
559 }
560 
567 {
568  /* If the length of both strings, plus the 2 '\0' terminations and 3 bytes of the packet
569  * are bigger than the MTU, just ignore the message. Better safe than sorry. It should
570  * never occur though as the longest strings are chat messages, which are still 30%
571  * smaller than SEND_MTU. */
572  if (strlen(origin) + strlen(string) + 2 + 3 >= SEND_MTU) return NETWORK_RECV_STATUS_OKAY;
573 
575 
576  p->Send_string(origin);
577  p->Send_string(string);
578  this->SendPacket(p);
579 
581 }
582 
588 {
589  /* At the moment we cannot transmit anything larger than MTU. So we limit
590  * the maximum amount of json data that can be sent. Account also for
591  * the trailing \0 of the string */
592  if (strlen(json) + 1 >= NETWORK_GAMESCRIPT_JSON_LENGTH) return NETWORK_RECV_STATUS_OKAY;
593 
595 
596  p->Send_string(json);
597  this->SendPacket(p);
598 
600 }
601 
604 {
606 
607  p->Send_uint32(d1);
608  this->SendPacket(p);
609 
611 }
612 
615 {
617 
618  for (uint i = 0; i < CMD_END; i++) {
619  const char *cmdname = GetCommandName(i);
620 
621  /* Should SEND_MTU be exceeded, start a new packet
622  * (magic 5: 1 bool "more data" and one uint16 "command id", one
623  * byte for string '\0' termination and 1 bool "no more data" */
624  if (p->size + strlen(cmdname) + 5 >= SEND_MTU) {
625  p->Send_bool(false);
626  this->SendPacket(p);
627 
629  }
630 
631  p->Send_bool(true);
632  p->Send_uint16(i);
633  p->Send_string(cmdname);
634  }
635 
636  /* Marker to notify the end of the packet has been reached. */
637  p->Send_bool(false);
638  this->SendPacket(p);
639 
641 }
642 
649 {
651 
652  p->Send_uint32(client_id);
653  p->Send_uint8 (cp->company);
654  p->Send_uint16(cp->cmd & CMD_ID_MASK);
655  p->Send_uint32(cp->p1);
656  p->Send_uint32(cp->p2);
657  p->Send_uint32(cp->tile);
658  p->Send_string(cp->text);
659  p->Send_uint32(cp->frame);
660 
661  this->SendPacket(p);
662 
664 }
665 
666 /***********
667  * Receiving functions
668  ************/
669 
671 {
672  if (this->status != ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
673 
674  char password[NETWORK_PASSWORD_LENGTH];
675  p->Recv_string(password, sizeof(password));
676 
678  strcmp(password, _settings_client.network.admin_password) != 0) {
679  /* Password is invalid */
680  return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
681  }
682 
683  p->Recv_string(this->admin_name, sizeof(this->admin_name));
684  p->Recv_string(this->admin_version, sizeof(this->admin_version));
685 
686  if (StrEmpty(this->admin_name) || StrEmpty(this->admin_version)) {
687  /* no name or version supplied */
688  return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
689  }
690 
691  this->status = ADMIN_STATUS_ACTIVE;
692 
693  DEBUG(net, 1, "[admin] '%s' (%s) has connected", this->admin_name, this->admin_version);
694 
695  return this->SendProtocol();
696 }
697 
699 {
700  /* The admin is leaving nothing else to do */
701  return this->CloseConnection();
702 }
703 
705 {
706  if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
707 
710 
711  if (type >= ADMIN_UPDATE_END || (_admin_update_type_frequencies[type] & freq) != freq) {
712  /* The server does not know of this UpdateType. */
713  DEBUG(net, 3, "[admin] Not supported update frequency %d (%d) from '%s' (%s).", type, freq, this->admin_name, this->admin_version);
714  return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
715  }
716 
717  this->update_frequency[type] = freq;
718 
720 }
721 
723 {
724  if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
725 
727  uint32 d1 = p->Recv_uint32();
728 
729  switch (type) {
730  case ADMIN_UPDATE_DATE:
731  /* The admin is requesting the current date. */
732  this->SendDate();
733  break;
734 
736  /* The admin is requesting client info. */
737  const NetworkClientSocket *cs;
738  if (d1 == UINT32_MAX) {
741  this->SendClientInfo(cs, cs->GetInfo());
742  }
743  } else {
744  if (d1 == CLIENT_ID_SERVER) {
746  } else {
747  cs = NetworkClientSocket::GetByClientID((ClientID)d1);
748  if (cs != NULL) this->SendClientInfo(cs, cs->GetInfo());
749  }
750  }
751  break;
752 
754  /* The admin is asking for company info. */
755  const Company *company;
756  if (d1 == UINT32_MAX) {
757  FOR_ALL_COMPANIES(company) {
758  this->SendCompanyInfo(company);
759  }
760  } else {
761  company = Company::GetIfValid(d1);
762  if (company != NULL) this->SendCompanyInfo(company);
763  }
764  break;
765 
767  /* The admin is requesting economy info. */
768  this->SendCompanyEconomy();
769  break;
770 
772  /* the admin is requesting company stats. */
773  this->SendCompanyStats();
774  break;
775 
777  /* The admin is requesting the names of DoCommands. */
778  this->SendCmdNames();
779  break;
780 
781  default:
782  /* An unsupported "poll" update type. */
783  DEBUG(net, 3, "[admin] Not supported poll %d (%d) from '%s' (%s).", type, d1, this->admin_name, this->admin_version);
784  return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
785  }
786 
788 }
789 
791 {
792  if (this->status == ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
793 
794  NetworkAction action = (NetworkAction)p->Recv_uint8();
795  DestType desttype = (DestType)p->Recv_uint8();
796  int dest = p->Recv_uint32();
797 
798  char msg[NETWORK_CHAT_LENGTH];
800 
801  switch (action) {
802  case NETWORK_ACTION_CHAT:
803  case NETWORK_ACTION_CHAT_CLIENT:
804  case NETWORK_ACTION_CHAT_COMPANY:
805  case NETWORK_ACTION_SERVER_MESSAGE:
806  NetworkServerSendChat(action, desttype, dest, msg, _network_own_client_id, 0, true);
807  break;
808 
809  default:
810  DEBUG(net, 3, "[admin] Invalid chat action %d from admin '%s' (%s).", action, this->admin_name, this->admin_version);
811  return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
812  }
813 
815 }
816 
817 /*
818  * Useful wrapper functions
819  */
820 
826 void NetworkAdminClientInfo(const NetworkClientSocket *cs, bool new_client)
827 {
831  as->SendClientInfo(cs, cs->GetInfo());
832  if (new_client) {
833  as->SendClientJoin(cs->client_id);
834  }
835  }
836  }
837 }
838 
844 {
848  as->SendClientUpdate(ci);
849  }
850  }
851 }
852 
858 {
862  as->SendClientQuit(client_id);
863  }
864  }
865 }
866 
873 {
877  as->SendClientError(client_id, error_code);
878  }
879  }
880 }
881 
887 void NetworkAdminCompanyInfo(const Company *company, bool new_company)
888 {
889  if (company == NULL) {
890  DEBUG(net, 1, "[admin] Empty company given for update");
891  return;
892  }
893 
897 
898  as->SendCompanyInfo(company);
899  if (new_company) {
900  as->SendCompanyNew(company->index);
901  }
902  }
903 }
904 
909 void NetworkAdminCompanyUpdate(const Company *company)
910 {
911  if (company == NULL) return;
912 
916 
917  as->SendCompanyUpdate(company);
918  }
919 }
920 
927 {
930  as->SendCompanyRemove(company_id, bcrr);
931  }
932 }
933 
934 
938 void NetworkAdminChat(NetworkAction action, DestType desttype, ClientID client_id, const char *msg, int64 data, bool from_admin)
939 {
940  if (from_admin) return;
941 
945  as->SendChat(action, desttype, client_id, msg, data);
946  }
947  }
948 }
949 
956 void NetworkServerSendAdminRcon(AdminIndex admin_index, TextColour colour_code, const char *string)
957 {
958  ServerNetworkAdminSocketHandler::Get(admin_index)->SendRcon(colour_code, string);
959 }
960 
966 void NetworkAdminConsole(const char *origin, const char *string)
967 {
971  as->SendConsole(origin, string);
972  }
973  }
974 }
975 
980 void NetworkAdminGameScript(const char *json)
981 {
985  as->SendGameScript(json);
986  }
987  }
988 }
989 
995 void NetworkAdminCmdLogging(const NetworkClientSocket *owner, const CommandPacket *cp)
996 {
997  ClientID client_id = owner == NULL ? _network_own_client_id : owner->client_id;
998 
1002  as->SendCmdLogging(client_id, cp);
1003  }
1004  }
1005 }
1006 
1011 {
1014  as->SendWelcome();
1015  }
1016 }
1017 
1023 {
1026  for (int i = 0; i < ADMIN_UPDATE_END; i++) {
1027  if (as->update_frequency[i] & freq) {
1028  /* Update the admin for the required details */
1029  switch (i) {
1030  case ADMIN_UPDATE_DATE:
1031  as->SendDate();
1032  break;
1033 
1035  as->SendCompanyEconomy();
1036  break;
1037 
1039  as->SendCompanyStats();
1040  break;
1041 
1042  default: NOT_REACHED();
1043  }
1044  }
1045  }
1046  }
1047 }
1048 
1049 #endif /* ENABLE_NETWORK */